실행 환경에 따른 env 파일 생성 env-cmd 패키지를 이용해서 Application 실행 환경에 따라 .env파일을 따로 정의해서 사용할 수 있다. 기존의 reddit-clone-app\client\.env 파일은 개발 환경에서 사용하기 위해 .env.development 파일로 파일명을 변경해준다. 이후 reddit-clone-app\client\.env.production 파일을 생성한다. 개발 환경 => .env.development 운영 환경 => .env.production reddit-clone-app\client\.env.production 파일의 내용은 아래와 같다. 현재 사용하고 있는...
AWS EC2 인스턴스 생성 AWS 계정을 생성하고 EC2 인스턴스를 프리티어(무료)로 사용하는 방법에 대해서는 사전에 블로그에 작성한 포스트를 참고하면 된다. 해당 포스트에서는 아마존 리눅스 2 이미지를 사용해 서버를 구성했으므로 필요에 따라 다른 이미지를 선택해도 무방하다. 무료로 AWS EC2 인스턴스 사용하기(프리 티어) AWS에 Docker 설치 내가 사용하는 AWS 서버 OS 이미지는 아마존...
유저 페이지 완성 UI 유저 페이지 생성 유저 페이지의 context path는 /u/[username]의 형태를 갖도록 한다. 이를 위해 reddit-clone-app\client\src\pages\u\[username].tsx 파일을 생성하고 아래 내용을 입력한다. import UserPage from 'react' const UserPage = () => { const router = useRouter(); // 사용자 이름을 URL에서 가져옴 const username = router.query.username; // swr 요청을 사용해...
Intersection observer 화면에 DOM 엘리먼트가 교차되는 것을 감시하고 개발자가 정의한 부분만큼 교차되었을 때 원하는 로직을 실행하기 위해 사용한다. threshold는 DOM엘리먼트가 뷰포트와 교차된 비율을 의미하며 해당 비율만큼 교차되었을 때 구문이 실행됨을 의미한다. 마지막 포스트가 화면에 교차되었을 때 다음 포스트 가져오기 reddit-clone-app\client\src\pages\index.tsx 파일에 아래 내용을 추가한다. ...생략 // 현재 감시중인 ELEMENT의 id값...
useSWRInfinite 이란 [useSWRInfinite 문서] https://swr.vercel.app/ko/docs/pagination#useswrinfinite SWR은 페이지 매김 및 무한 로딩과 같은 일반적인 UI 패턴을 지원하기 위해 전용 API useSWRInfinite를 제공한다. 무한 스크롤 UI패턴의 경우 지속적으로 다음 페이지 데이터를 가져오기 위한 용도로 사용된다. [useSWRInfinite 반환 값] data : 요청을 통해 각 페이지 응답 값의 배열 error : useSWR의 error와 동일...
포스트 나열하기 커뮤니티 상세 페이지 진입 시 해당 커뮤니티에 작성된 포스트들을 나열하기 위해 커뮤니티 상세 페이지 reddit-clone-app\client\src\pages\r\[sub].tsx 파일에 아래 내용을 추가한다. ...생략 import PostCard from '../../components/PostCard'; ...생략 let renderPosts; if(!sub) { // 커뮤니티 존재하지 않을 때 renderPosts = <p className='text-lg text-center'>로딩중...</p> } else if(sub.posts.length === 0) { // 포스트가 없을...
투표 적용 시 즉시 업데이트 코멘트와 마찬가지로 useSWR 사용 구문에서 mutate를 가져오고 이를 투표 요청하는 vote함수에서 실행해주면 된다. reddit-clone-app\client\src\pages\r\[sub]\[identifier]\[slug].tsx 파일에 아래 내용을 추가해 본다. ...생략 const { data: post, error, mutate } = useSWR<Post>(identifier && slug ? `/posts/${identifier}/${slug}` : null) ...생략 const vote = async (value: number, comment?:Comment) => {...
포스트, 코멘트 투표 UI 포스트와 코멘트 투표 완성 UI는 아래와 같다 포스트, 코멘트 투표 UI 템플릿 작성 포스트 내용 페이지 내 포스트, 코멘트 투표 UI를 추가해야 하므로 reddit-clone-app\client\src\pages\r\[sub]\[identifier]\[slug].tsx 파일에 아래 내용을 추가한다. [포스트 투표 부분] ...생략 {post && ( <> <div className="flex"> {/* 투표 가능 부분 */} <div className="flex-shrink-0 w-10...
useSWR mutate 란? 캐시 된 데이터를 개발자가 원하는 시점에서 갱신하기 위한 함수이다. useSWRConfig() hook으로부터 mutate 함수를 얻을 수 있으며, mutate(key)를 호출하여 동일한 키를 사용하는 다른 SWR hook*에게 갱신 메시지를 전역으로 브로드캐스팅할 수 있습니다. 포스트 코멘트 작성에서 mutate 적용 현재 댓글 작성 후 작성한 코멘트가 즉시 화면에 갱신되지 않는다. 새로고침을 수행하면...
코멘트 리스트 가져오기 포스트 내용 페이지 진입 시 SWR을 사용해서 해당 포스트에 작성된 코멘트를 가져온다. reddit-clone-app\client\src\pages\r\[sub]\[identifier]\[slug].tsx 파일에 아래 내용을 추가한다. ...생략 const PostPage = () => { const router = useRouter(); const { identifier, sub, slug } = router.query; const { authenticated, user } = useAuthState(); const [ newComment, setNewComment...
포스트 댓글 UI 로그인 된 상태에서는 댓글 작성이 가능하도록, 로그인이 되지 않은 상태에서는 댓글 작성을 위해 로그인 해주세요 메시지를 출력한다. 완성된 UI는 아래와 같다. 포스트 댓글 UI 템플릿 작성 댓글은 포스트 내용 페이지에 진입 시 작성할 수 있어야 하므로 reddit-clone-app\client\src\pages\r\[sub]\[identifier]\[slug].tsx 파일에 템플릿을 아래와 같이 추가한다. ...생략 // 로그인 여부에 따라...
포스트 페이지 결과 UI 포스트 페이지 생성 포스트 페이지의 context path는 /r/{SubName}/{identifier}/{slug}의 형태를 띌 것이므로 reddit-clone-app\client\src\pages\r\[sub]\[identifier]\[slug].tsx 파일을 생성하고 아래 내용을 입력한다. import axios from "axios"; import { useRouter } from "next/router" import useSWR from 'swr' import { Post } from "../../../../types"; const PostPage = () => { const router =...
포스트 Create 페이지 기능 앞서 생성했던 포스트 Create 생성 페이지에서 생성하기 버튼을 통해 form 태그의 onSubmit 이벤트가 발생했을 때 동작하게 될 submitPost 함수를 아래와 같이 추가한다. reddit-clone-app\client\src\pages\r\[sub]\create.tsx ...생략 // 페이지 URL내의 커뮤니티 이름(subName)을 사용하기 위해 추가 const router = useRouter(); const { sub: subName } = router.query; const submitPost= async...
포스트 Create 페이지 UI 포스트 Create 페이지 UI 구현 포스트 Create 페이지의 context path를 /r/{커뮤니티명}/create 으로 해주기 위해 reddit-clone-app\client\src\pages\r\[sub]\create.tsx 경로에 파일을 생성한다. 아래의 내용을 입력한다. import axios from 'axios'; import { GetServerSideProps } from 'next'; import React, { useState } from 'react' const PostCreate = () => { // 포스트...
사이드 바 컴포넌트 생성, 적용 reddit-clone-app\client\src\components\SideBar.tsx 경로에 파일을 생성하고 아래 내용을 입력한다. import dayjs from 'dayjs'; import Link from 'next/link'; import React from 'react' import { useAuthState } from '../context/auth'; const SideBar = ({ sub }) => { const { authenticated } = useAuthState(); return ( <div className='hidden w-4/12 ml-3...
ref를 사용하여 이미지 올리기 먼저 DOM상에 이미지 파일을 업로드하기 위해 reddit-clone-app\client\src\pages\r\[sub].tsx 파일에 아래 내용을 추가해준다. import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; ...생략 const fileInputRef = useRef<HTMLInputElement>(null); ...생략 return ( <> {sub && <> <div> // 추가 부분 <input type="file" hidden={true} ref={fileInputRef} onChange={uploadImage} /> ...생략 fileInputRef :...
커뮤니티 상세 페이지 UI 템플릿 reddit-clone-app\client\src\pages\r\[sub].tsx 경로의 파일을 생성하고 아래 내용을 입력한다. import axios from 'axios' import { useRouter } from 'next/router'; import React from 'react' import useSWR from 'swr'; const SubPage = () => { const fetcher = async (url: string) => { try { const res = await...
SWR (Stale-while-revalidate) 란? 데이터를 가져오기 위한 React Hook 라이브러리이다. SWR은 원격 데이터를 가져올 때 캐싱된 데이터가 있으면 그 데이터를 먼저 반환(stale)한 다음 가져오기 요청(revalidate)을 보내고, 마지막으로 최신 데이터와 함께 제공하는 라이브러리입니다. **캐시 데이터를 제공함으로써 사용자에게 응답속도가 빠르다 ** SWR 사용법 res = useSWR() 형태로 사용하며 React Hook으로, 주 인자로 key와...
커뮤니티 리스트 UI reddit-clone-app\client\src\pages\index.tsx 파일을 아래와 같이 수정한다. import type { NextPage } from 'next' import Link from 'next/link' const Home: NextPage = () => { return ( <div className='flex max-w-5xl px-4 pt-5 mx-auth'> {/* 포스트 리스트 */} <div className='w-full md:mr-3 md:w-8/12'></div> {/* 사이드바 */} <div className='hidden w-4/12 ml-3 md:block'>...
상단바 UI 생성 import axios from 'axios'; import Link from 'next/link' import React from 'react' import { useAuthDispatch, useAuthState } from '../context/auth' const NavBar: React.FC = () => { const { loading, authenticated } = useAuthState(); const dispatch = useAuthDispatch(); const handleLogout = () => { axios.post("/auth/logout") .then(() => {...
인증에 따른 제한 현재 로그인 전 브라우저 쿠키에 token이 없는 상태로 /subs/create 페이지에 접속이 가능하다. 인증되지 않은 사용자가 커뮤니티 생성 페이지에 접근이 가능하다는 것이다. 이러한 문제점을 해결하기 위해 Next.js의 getServerSideProps를 사용한다. reddit-clone-app\client\src\pages\subs\create.tsx 파일에 아래 내용을 추가한다. ...생략 // 서버 사이드 렌더링으로 서버 요청 시 데이터를 불러온다 export const getServerSideProps: GetServerSideProps...
State 생성 reddit-clone-app\client\src\pages\subs\create.tsx 파일에서 이전에 UI를 위한 템플릿을 작성했다. UI의 기능 구현을 위해 State를 선언하고 필요 모듈을 import한다. // 필요 모듈 선언 useRouter, useState, axios import axios from 'axios'; import { useRouter } from 'next/router'; import React, { FormEvent, useState } from 'react' import InputGroup from '../../components/InputGroup' const SubCreate =...
커뮤니티 생성 페이지 UI 현재 개발하고 있는 Reddit-clone-app에서 커뮤니티를 생성하는 페이지의 UI는 아래와 같다. 커뮤니티 생성 페이지 UI 파일 생성 reddit-clone-app\client\src\pages\subs\create.tsx 폴더 구조와 파일을 생성하고, 아래 소스를 입력한다. 구분 폴더명이 subs인 이유는 Reddit 서비스에서 커뮤니티를 Subreddits 라고 지칭하기 때문이다. import React from 'react' import InputGroup from '../../components/InputGroup' const SubCreate =...
사용자 정보를 Context에 담아 사용하는 이유 React Context를 사용하지 않고 User정보를 각각의 React 컴포넌트에서 사용하기 위해서는 컴포넌트 간 User 정보를 주고 받는 작업이 매번 이루어진다. 그러나 React Context에서 User 정보를 관리하게 되면 컴포넌트 간 데이터를 주고 받을 필요 없이 로그인 후 언제든 React Context에서 User 정보를 사용 가능하다. React Context...
로그인 후 Client Cookie 처리 로그인 요청 후 response 쿠기에 정상적으로 jwt 토큰이 담겨 오는 것을 확인했다. 그러나 개발자도구 > Application 탭 내 jwt 토근이 저장되지 않는다. 이유는 Server에서 로그인 후 response 객체에 쿠키를 Set 하는 과정에서 옵션넣어줘야 한다. reddit-clone-app\server\src\routes\auth.ts 파일에 아래 내용을 추가한다. ...생략 const login = async (req...
로그인 페이지 기능 생성 reddit-clone-app\client\src\pages\login.tsx 파일에 아래의 내용을 추가한다. import Axios from 'axios'; ...생략 import React, { FormEvent, useState } from 'react' ...생략 const login = () => { // State 생성 const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [errors, setErrors] = useState<any>({}); // handleSubmit 함수...
로그인 페이지 UI 생성 reddit-clone-app\client\src\pages\login.tsx 파일을 생성하고 아래의 내용으로 편집한다. import React from 'react' import InputGroup from '../components/InputGroup' const Login = () => { return ( <div className='bg-white'> <div className='flex flex-col items-center justify-center h-screen p-6'> <div className='w-10/12 mx-auto md:w-96'> <h1 className='mb-2 text-lg font-medium'>로그인</h1> <form onSubmit={handleSubmit}> <InputGroup placeholder='Username' value={username} setValue={setUsername} error={errors.username}...
register.tsx 코드 작성 import Link from 'next/link' import React, {useState} from 'react' import InputGroup from '../components/InputGroup' const Register = () => { const [email, setEmail] = useState(""); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [errors, setErrors] = useState<any>({}); return ( <div className="bg-white"> <div className='flex flex-col items-center...
회원가입 UI 페이지 완성 모습 register.tsx 파일 생성 reddit-clone-app/client/src/pages/register.tsx 파일을 생성한다. UI를 다음과 같이 작성한다. import Link from 'next/link' import React from 'react' const register = () => { return ( <div className="bg-white"> <div className='flex flex-col items-center justify-center h-screen p-6'> <div className='w-10/12 mx-auto md:w-96'> <h1 className='mb-2 text-lg font-medium'>회원가입</h1> <form> <button...
Tailwind CSS 적용을 위한 모듈 설치하기 reddit-clone-app/client 경로에서 npm 모듈 설치 npm i -D postcss-preset-env tailwindcss Tailwind 설정 파일 생성 reddit-clone-app/client 경로 터미널에서 아래 명령어 입력 npx tailwind init 아래와 같이 tailwind.config.js 파일이 생성됩니다. PostCSS 빌드 적용을 위한 postcss 설정 파일 생성 reddit-clone-app/client 경로 터미널에서 아래 명령어 입력 touch postcss.config.js...
Comment 기능 화면 Comment 컬럼 Comment Entity 작성 import { Exclude, Expose } from 'class-transformer'; import { BeforeInsert, Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; import { makeId } from '../utils/helpers'; import BaseEntity from './Entity'; import Post from './Post'; import { User } from './User'; import Vote from...
Vote 기능 화면 Vote 테이블 컬럼 Vote 엔티티 작성 import { Column, Entity, JoinColumn, ManyToOne } from "typeorm" import BaseEntity from './Entity'; import Post from "./Post"; import { User } from "./User"; @Entity("votes") export default class Vote extends BaseEntity { @Column() value: number; @ManyToOne(() => User) @JoinColumn({name: "username", referencedColumnName: "username"})...
포스트 테이블 컬럼 포스트 엔티티 작성 import { Exclude, Expose } from "class-transformer"; import { BeforeInsert, Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from "typeorm"; import { makeId, slugify } from "../utils/helpers"; import BaseEntity from "./Entity"; import Sub from "./Sub"; import { User } from "./User"; import Vote from "./Vote";...
URI, URL, URN 란? URL과 URI의 차이점 URL은 어떻게 리소스를 얻을 것이고 어디에서 가져와야 하는지 명시하는 URI이다. URN은 리소스를 어떻게 접근할 것인지 명시하지 않고 경로와 리소스 자체를 특정하는 것을 목표로 하는 URI이다.
개요 엔티티를 생성할 때 상태뿐 아니라 행위까지 엔티티 내 정의하여 행위의 반환값까지 프론트엔드에서 사용할 수 있게 한다. 이를 풀어서 설명하면 데이터베이스에서 User객체를 조회할 때 아래 User 클래스의 행위인 getter : name(), getFullName() 값을 함께 조회하게 된다. 마찬가지로 조회 시 제외해야할 행위에 대해 정의할 수 있다. import { Expose } from...
class-tranformer 모듈이란? Class-transformer를 사용하면 Plain object를 클래스 인스턴스로 변환할 수 있다. class-transformer를 이용해서 자바스크립트 리터럴 객체를 클래스의 인스턴스로 변경해주면 좋은 점 아래와 같은 자바스크립트 객체가 있다고 가정한다. 이 객체들의 firstName과 lastName을 합치고자할 때를 예를 들어 보자 class-transformer 없이 구현 toFullName(user) 함수를 정의하고 FullName 반환값을 구하는 과정이 필요하다. class-transformer 사용해서 구현...
테이블 컬럼 엔티티 작성 import { Expose } from "class-transformer"; import { Column, Entity, Index, JoinColumn, ManyToMany, ManyToOne, OneToMany } from "typeorm"; import BaseEntity from './Entity'; import Post from "./Post"; import { User } from "./User"; @Entity("subs") export default class Sub extends BaseEntity { @Index() @Column({ unique: true }) name:...
User 테이블 컬럼 User 엔티티 작성 import { IsEmail, Length } from "class-validator" import { Entity, Column, Index, OneToMany, BeforeInsert } from "typeorm" import BaseEntity from './Entity'; import bcrypt from 'bcryptjs'; import Vote from "./Vote"; import Post from "./Post"; @Entity("users") export class User extends BaseEntity { @Index() @IsEmail(undefined, { message:...
레딧 앱 E-R Diagram 강의의 주제인 레딧 앱의 DB E-R Diagram이다. Entity를 생성하는 이유 ORM의 사용 없이 DB 테이블을 생성하기 위해서는 DDL (Database Definition Language)를 사용하여 일일히 테이블을 생성해줘야하지만, TypeORM을 사용할 때는 Entity Class가 데이터 베이스 테이블로 변환되기 때문에 Class를 생성한 컬럼을 정의해주면 된다. E-R Diagram와 일치하는 Entity 생성 Users...
Next.js App 생성 Reddit 프로젝트 폴더 구조를 다음과 같이 생성한다. reddit-clone-app client server Visual Studio Code (편집기)를 사용해 client 폴더 경로를 열고, 터미널을 실행한다. 아래 명령어를 사용해 Next.js App을 client 폴더에 생성한다. npx create-next-app@latest --typescript ./ Node.js 백엔드 서버 생성 Visual Studio Code (편집기)를 사용해 server 폴더 경로를 열고, 터미널을...
getStaticProps를 이용한 포스트 리스트 나열 props로 포스트 데이터 가져오기 index.tsx // getStaticProps 의 반환값(포스팅 md파일 데이터)을 빌드 시 가져옴 const Home = ({allPostsData}: { allPostsData: { date: string title: string id: string }[] }) => { return ( <HTML> ... </HTML> ) } export default Home // lib/post.js 에서 생성한...
Markdown 파일이란? (.md 파일) Markdown은 텍스트 기반의 마크업 언어로 쉽게 쓰고 읽을 수 있으며 HTML로 변환이 가능하다. 특수 기호와 문자를 이용한 매우 간단한 구조의 무넙을 사용하여 웹에서도 보다 빠르게 컨텐츠를 작성하고 직관적으로 인식할 수 있다. 마크다운이 최근 각광받기 시작한 이유는 깃헙에서 사용하는 README.md 덕분이다. 마크다운을 통해서 설치방법, 소스 코드 설명,...
Data Fetching 보통 React JS에서 데이터를 가져올 때 useEffect 안에서 가져온다. 하지만 Next JS 에서는 다른 방법을 사용해서 가져오는데 무엇인지 알아보자. getStaticProps Static Generation으로 빌드(build)할 때 데이터를 불러온다. (데이터를 미리 만들어 둠) getStaticProps를 사용해야 할 때 페이지를 렌더링하는 데 필요한 데이터. 사용자의 요청보다 먼저 build 시간에 필요한 데이터를 가져올 때...
Next JS 와 Pre-rendering Next JS는 모든 페이지를 pre-rendering한다. pre-rendering한다는 의미는 모든 페이지를 위한 HTML을 Client 사이드에서 Javascript로 처리하기 전, 사전에 생성한다는 것을 의미한다. 이렇게 하기 때문에 SEO(Search Engine Obtivization), 검색 엔진 최적화에 유리. Pre-rendering 테스트 브라우저에서 Javascript 사용을 하지 못하도록 만들면 Client 사이드(브라우저)에서 Pre-rendering한 HTML을 확인할 수 있다. 1....
Next JS 기본 파일 구조 npx create-next-app@latest --typescript 명령을 사용하여 Next JS app을 생성하면 아래와 같은 폴더 구조를 갖게된다. Next JS App의 기본 폴더 구조에 대해 알아보자. pages 폴더 pages 폴더 내에 Application 페이지들을 생성. index.tsx가 처름 ”/”(루트) 페이지가 된다. _app.tsx는 공통되는 레이아웃을 작성. (header, footer와 같은) URL을 통해 특정...
Next JS란? React의 SSR(Server Side Rendering)을 쉽게 구현할 수 있게 도와주는 간단한 프레임워크. (리액트는 라이브러리) React로 개발할 때 SPA(Single Page Application)을 이용하여 CSR(Client Side Rendering)을 하기 때문에 좋은 점도 있지만 단점도 있는데 그 부분이 바로 검색 엔진 최적화(SEO) 부분이다. Client Side Rendering을 하면 첫페이지에서 빈 html을 가져와 JS파일을 해석하여 화면을...
No projects with this tag.