Skip to content

Latest commit

 

History

History
354 lines (233 loc) · 19.2 KB

README.md

File metadata and controls

354 lines (233 loc) · 19.2 KB

VanilLog: 일상을 기록하는 가장 심플한 블로그 📓

💁‍♀️ 소개

VanilLog는 개인의 블로그 글을 관리하고 공유할 수 있는 플랫폼입니다. NextJS 13의 최신 기능과 함께, 사용자는 자신의 블로그에서 쉽고 빠르게 게시글을 작성하고 관리할 수 있습니다.

📹 VanilLog 시연 영상
  • 메인화면

    • 로그인하지 않은 경우, 포스트 조회 및 검색 기능만 이용할 수 있습니다.

      2023-10-13.10.49.03.mov
  • 로그인

    • 상단바의 'Login' 버튼이나 사이드바의 '로그인을 해주세요.' 버튼을 클릭해 로그인 화면으로 이동합니다.

    • 구글 로그인 시 자동으로 회원가입 및 로그인됩니다.

    • 가입 초기에 설정되는 닉네임과 프로필 이미지는, 연계된 구글 계정에서 가져온 닉네임과 이미지입니다.

      2023-10-13.10.43.40.mov
  • 포스트 작성하기

    • 제목을 작성합니다.

    • 내용에는 텍스트나 이미지를 추가합니다.

    • '저장하기' 버튼을 클릭하여 포스트를 게시합니다.

    • MyLog에서 작성한 포스트를 확인할 수 있습니다.

      2023-10-13.10.56.15.mov
  • 포스트 수정하기

    • 포스트 상세 페이지에서 '수정하기' 버튼을 클릭해 수정 화면으로 이동합니다.

    • 제목이나 내용을 변경합니다.

    • '저장하기' 버튼을 클릭하여 수정사항을 저장합니다.

    • MyLog에서 수정된 포스트를 확인할 수 있습니다.

      2023-10-13.10.58.02.mov
  • 포스트 삭제하기

    • 포스트 상세 페이지에서 '삭제하기' 버튼을 클릭합니다.

    • '확인' 버튼을 클릭하여 포스트를 삭제합니다.

    • MyLog에서 삭제된 포스트를 확인할 수 있습니다.

      2023-10-13.10.59.32.mov
  • 댓글 작성

    • 포스트 상세 페이지 하단에 댓글을 입력한 후 '댓글 작성' 버튼을 클릭합니다.

    • 작성한 댓글을 확인할 수 있습니다.

      2023-10-13.11.03.08.mov
  • 댓글 수정, 삭제

    • 수정하고자 하는 댓글의 '수정' 버튼을 클릭합니다.

    • 댓글을 수정한 후 '확인' 버튼을 클릭합니다.

    • 수정된 댓글을 확인할 수 있습니다.

    • 삭제하고자 하는 댓글의 '삭제' 버튼을 클릭합니다.

    • 삭제된 댓글을 확인할 수 있습니다.

      2023-10-13.11.04.05.mov
  • 통합 검색

    • 메인화면 내 우측 상단 검색란에 검색어를 입력한 후 엔터를 누릅니다.

    • 검색어와 관련된 포스트들이 표시됩니다.

      2023-10-13.11.10.22.mov
  • 블로그내 검색

    • 특정 블로그 내 우측 상단 검색란에 검색어를 입력한 후 엔터를 누릅니다.

    • 해당 블로그 내에서 검색어와 관련된 포스트들이 표시됩니다.

      2023-10-13.11.09.11.mov
  • 프로필

    • 좌측 상단의 햄버거 아이콘을 클릭합니다.

    • 사이드바에서 '프로필 정보/수정' 버튼을 클릭합니다.

      2023-10-13.11.11.43.mov
  • 프로필 사진 변경

    • '사진 업로드/변경' 버튼을 클릭합니다.

    • 사진 변경란에서는 변경된 프로필 사진이 바로 반영되고, 상단바의 프로필 사진은 페이지를 새로고침하면 확인할 수 있습니다.

      2023-10-13.11.12.53.mov
  • 닉네임 변경

    • 내 닉네임에 있는 '변경하기' 버튼을 클릭합니다.

    • 닉네임을 변경한 후 '저장' 버튼을 클릭합니다. (다른 유저와 중복되는 닉네임으로는 변경할 수 없습니다.)

    • 내 닉네임란에서는 변경된 닉네임이 바로 반영되고, 상단바의 닉네임은 페이지를 새로고침하면 확인할 수 있습니다.

      2023-10-13.11.13.49.mov

👀 목차


💡 프로젝트 동기

1. 학습 목표

  • JavaScript를 기반으로 프론트엔드 및 백엔드를 실제로 구현하며 학습 심화
  • 기존에 학습한 기술 스택의 효과적 활용
  • 사용자 중심의 웹서비스 개발

2. 블로그 아이디어

블로그는 프론트엔드와 백엔드 모두에서 다양한 기능과 요구사항을 가진 서비스 중 하나입니다. 기존에 배운 기술들을 다양한 방면에서 활용하면서 실력을 향상 시킬 수 있는 좋은 아이디어라고 판단하였습니다.

3. 웹 애플리케이션 'VanilLog'

학습 목표와 아이디어를 기반으로, 사용자들이 자신의 일상을 자유롭게 공유할 수 있는 웹 애플리케이션인 'VanilLog'가 탄생하게 되었습니다.


🚧 도전 및 문제 해결

3주 동안 개발을 진행하면서 겪었던 핵심적인 문제들과 해결 방안을 정리해 보았습니다.

1. Next JS 13 + App Router

1) 어떤 프레임워크를 사용해서 구현할까?

블로그를 구현하는데에 다음과 같은 요구사항이 있었습니다.

  • 검색 엔진에서 사용자가 키워드를 검색하였을 때, 이 블로그의 게시글을 더 많은 사용자에게 더 높은 빈도로 노출시키기 위해 SEO 적용이 필요
  • 백엔드에서 하는 역할을 살펴보니 api호출 외의 별다른 작업이 없음

리액트는 기본적으로 CSR으로 돌아가기 때문에 SEO에 취약했습니다. 따라서 위와 같은 요구사항을 만족하기 위해 SEO를 위한 SSR을 제공하고, api를 클라이언트와 한번에 관리하기 위해 'Next.js'를 사용하기로 결정했습니다.

2) 타 라이브러리와의 호환성 문제:

'Next.js'를 사용하는 과정에서 운영사의 권고로 'Next.js' 13버전 App Router 방식을 사용했는데 초기에는 몰랐지만 프로젝트를 진행하는 과정에서 문제가 있었습니다. 'Next.js' 13 버전은 2022년에 출시되어, 관련 자료가 상대적으로 부족했습니다. 페이지 라우터와의 차이를 명확하게 인지하지 못하였고, 이로 인한 러닝 커브가 있었습니다. 또한 비교적 새로 나온 방식이다 보니 저희가 쓰려는 다른 라이브러리들에서 App Router방식을 지원하지 않은 경우도 많았습니다. 하지만 프로젝트가 이미 진행이 된 상태에서 Pages Router 방식으로 변경하기에는 시간이 많이 소요될 것으로 예상되었습니다. 기간을 고려하여 변경하지 않고 App Router 방식에서 해결할 수 있게 진행하였습니다.

3) 해결 방안:

공식 문서를 주로 참고하여 학습하였고, Next.js github issues들을 찾아보며 해결 되지 않은 문제와 다른 유저들이 해결한 방법들을 찾아보며 사용법을 숙달하였습니다. 그 결과 App Router 방식을 유지하고서 블로그 개발을 완료할 수 있었습니다.


2. 로그인 및 회원가입

1) 초기 기획과 문제점:

Firebase와 'next-firebase-auth': 초기 기획 단계에서, 로그인 및 회원가입 기능을 Firebase의 authentication 기능과 ‘next-firebase-auth’를 사용하여 구현하기로 기획하였습니다. 하지만 두 방식에는 다음과 같은 문제가 있었습니다.

  • Firebase: 이전에 Firebase를 이용한 DB 작업을 해보았기에 러닝 커브가 낮을 것이라고 판단하였으며 'firebase-auth'의 npm install 횟수도 다른 라이브러리들보다 월등히 높아서 신뢰할 수 있다고 판단했습니다. Firebase는 제휴 id 공급업체와 통합하여 사용자를 인증하며 인증 SDK는 구글, 애플, 깃허브 등을 지원하는 것으로 확인하였습니다. 이러한 이유로 사용하려고 했으나, 해결되지 않은 이슈를 발견하였습니다. 이슈를 우회하는 방법으로 구현을 해본 결과, 로그인 과정이 매우 느리다는 단점을 발견하였습니다.

  • next-firebase-auth: Next.js 13 + App Router에서 ‘next-firebase-auth’ 라이브러리가 제대로 작동하지 않았습니다. 이에 대해 논의 중인 PR(링크)을 확인하고 그 이유를 알 수 있었습니다. 새로운 App Router 접근 방식은 서버 컴포넌트를 중심으로 페이지나 레이아웃에서 데이터 가져오기 작업을 진행합니다. 이를 통해 전체 앱에서 인증 상태를 공유할 수 있도록 개발되었습니다. 하지만 이러한 변화로 인해 getServerSideProps, getStaticProps 및 getInitialProps를 제거하게 되어 기존의 인증 방법(서버 측에서 이 값들을 통해 인증 상태를 설정)과 호환되지 않는 것으로 파악하였습니다.

2) 해결 방안 모색:

  • NextAuth 도입의 필요성: 따라서 다른 방법을 모색해야 했습니다. 조사 및 회의를 거쳐 NextAuth를 사용하기로 결정하였습니다. NextAuth는 64가지의 주요 회사들의 소셜 로그인을 지원하고 있으며 providers 옵션 추가를 통해 간편하게 소셜 로그인을 적용 가능합니다. 보안 면에서는, 유저 데이터를 지켜주도록 쿠키 정책 제한, JWT encryption, CSRF 방지 토큰 사용 등의 기능을 제공합니다. 또한 초기 개발 단계에서부터 Next.js와 Serverless를 지원하도록 설계되었기 때문에 이 프로젝트와 호환이 잘 될 것으로 예상되었습니다.

3) NextAuth를 사용한 로그인 및 회원가입 기능 구현:

  • 라이브러리 도입: 프로젝트에 NextAuth 라이브러리를 도입했습니다.

  • 세션 관리: ‘next-auth/react’에서 제공하는 useSession을 사용하여 유저 세션 정보를 관리하였습니다. 이를 통해 로그인 상태에 따라 다양한 UI 및 기능에 접근 권한을 제어할 수 있었습니다.

  • 로그인 & 로그아웃 버튼 구현: ‘next-auth/react’에서 제공하는 signInsignOut 함수를 사용하여 로그인 및 로그아웃 버튼의 기능을 구현하였습니다.

  • 서버 인증 처리: 서버에서의 인증 처리를 위해 NextAuth를 사용하여 Google Provider를 설정하였습니다. 이를 통해 Google 로그인을 통한 사용자 인증이 가능하게 되었습니다. 또한, 사용자가 처음 로그인하는 경우, 해당 유저의 정보를 데이터베이스에 저장하는 로직도 구현하였습니다.

  • 미들웨어 설정: 특정 경로에서 로그인 인증이 필요한 경우를 처리하기 위해 ‘next-auth/middleware’를 사용하여 미들웨어를 설정하였습니다. 이를 통해 프로필 페이지나 포스트 에디터 등의 특정 페이지에 대한 접근 권한을 제어하였습니다.

  • SessionProvider 적용: 애플리케이션 전체에서 세션 정보에 쉽게 접근하기 위해 ‘next-auth/react’에서 제공하는 SessionProvider를 사용하여 앱의 레이아웃을 감싸주었습니다.

4) 기능 구현 결과:

App Router 방식 도입으로 'next-firebase-auth' 라이브러리 도입이 어려웠던 상황에서, NextAuth의 도입을 통해 로그인 및 회원가입 기능이 원활하게 작동하게 되었습니다. 또한 사용자 인증 및 세션 관리 문제를 해결할 수 있었으며 Firebase의 authentication으로 구현했을 때보다 훨씬 빠른 속도로 로그인 및 회원가입이 진행되었습니다. 외에도 세션 값의 데이터 구조가 더 간결해져서 개발 과정에서 편리하게 이용할 수 있다는 장점이 있었습니다.


3. 이미지 저장: AWS S3

이미지 업로드 기능을 구현하는데에 다음과 같은 요구사항이 있었습니다

1) 사용자가 업로드한 이미지를 어떻게 안전하게 보관할까?

  • 사용자의 이미지 데이터는 손실 없이 안전하게 보관되어야 합니다.
  • 이미지 데이터는 언제든지 빠르게 접근 및 검색이 가능해야 합니다.
  • 시스템 확장성을 고려하여 대량의 이미지 데이터도 효율적으로 관리할 수 있어야 합니다.
  • 데이터 보안 및 프라이버시를 위해 적절한 접근 제어 및 암호화 기능이 필요합니다.

프로젝트의 확장성과 데이터의 안정성을 고려하여 클라우드 기반의 스토리지 솔루션을 선택하게 되었습니다.

2) CORS 정책 제한 문제 발생:

웹 애플리케이션에서 S3 버킷의 이미지 리소스에 접근하려고 할 때, 교차 출처 리소스 공유(CORS) 정책으로 인해 접근이 제한되는 문제가 발생했습니다.

3) 해결 방안:

(a) 파일 업로드

사용자가 제출한 폼 데이터를 분석하여 이미지 파일을 추출했습니다. 해당 파일을 버퍼로 변환한 후, S3.putObject 메서드를 사용하여 S3에 업로드했습니다. AWS S3는 대용량 데이터를 안전하게 저장할 수 있는 서비스로, 데이터의 손실 없이 장기간 보관이 가능합니다. 또한, S3의 확장성과 높은 내구성으로 인해 대규모 웹 애플리케이션에서도 빠른 속도로 데이터에 접근할 수 있습니다. 이러한 이유로 S3를 선택하여 파일 업로드를 처리하였습니다.

const fileStream = file.stream();
let fileBuffer = Buffer.alloc(0);

for await (const chunk of fileStream) {
  fileBuffer = Buffer.concat([fileBuffer, chunk]);
}

const s3Params = {
  Bucket: bucketName,
  Key: fileName,
  Body: fileBuffer,
  ContentType: fileType,
  ACL: 'public-read',
};

await s3Data.putObject(s3Params);

(b) CORS 설정

getSignedUrl 메서드를 사용하여 시간 제한이 있는 URL을 생성했습니다. 이를 통해 웹 애플리케이션에서 S3 버킷의 리소스에 접근할 수 있게 되었습니다. 웹 애플리케이션과 S3 버킷은 다른 도메인에서 호스팅되므로, 보안 정책으로 인해 직접적인 리소스 접근이 제한됩니다. 이를 해결하기 위해 URL을 생성하여 일정 시간 동안만 리소스에 접근할 수 있도록 했습니다. 이 방법은 리소스의 보안을 유지하면서도 필요한 경우에만 접근을 허용하는 효과적인 방법입니다.

const signedUrl = await new Promise((resolve, reject) => {
  s3Data.getSignedUrl('putObject', s3Params, (err, data) => {...});
});

📅 일정

1주차
  • 아이디어 회의
  • 기술 스택 조사 및 선정
  • Git 작업 플로우 결정
  • 협업 규칙 정립
  • Mockup 제작 및 디자인 설계
  • DB 스키마 설계
  • API Docs 작업
  • 개발 일정 칸반 보드 작성
  • 개발 초기 세팅
2주차 ~
  • 웹 사이트 구현
  • 리팩토링 및 버그 픽스
  • 테스트 코드 작성
  • 팀 프로젝트 발표
  • 리드미 작성
  • 배포

🛠️ 기술 스택

React Next.js Next Auth AWS SDK Axios TailwindCSS ESLint Prettier


🗝 세팅 방법

시작하기 전에, 루트 디렉토리에 .env 파일을 만들고 아래와 같이 설정해주세요.

MONGODB_URI=                 # MongoDB 데이터베이스에 연결하기 위한 URI
NEXTAUTH_URL=                # NextAuth가 사용할 콜백 URL 및 서버의 주소
GOOGLE_CLIENT_ID=             # Google OAuth 인증을 위한 클라이언트 ID
GOOGLE_CLIENT_SECRET=         # Google OAuth 인증을 위한 클라이언트 키
NEXTAUTH_SECRET=             # NextAuth 세션을 암호화하기 위한 키
AWS_ACCESS_KEY_ID=           # AWS 서비스에 액세스하기 위한 사용자의 액세스 키 ID
AWS_SECRET_ACCESS_KEY=       # AWS 서비스에 액세스하기 위한 사용자의 액세스 키
AWS_REGION=                  # AWS S3 버킷이 위치한 리전 정보
AWS_S3_BUCKET_NAME=          # 이미지 등의 리소스를 저장할 AWS S3 버킷의 이름

👥 팀 멤버

문의 사항이 있으시다면, 아래의 이메일로 연락주세요.


🔗 Repository Link


📌 기타 사항

  • 라이센스

    이 프로젝트는 MIT 라이센스를 따릅니다. 더 자세한 내용은 LICENSE 파일을 참고해주세요.