SPRINT4

권나현·2025년 7월 15일

스프린트4 미션 목표

  • 이번 스프린트의 핵심은 “유저 인증/인가 시스템”을 직접 구현해보는 것이다.
  • 여기에 추가로 Refresh Token과 Prisma 관계형 DB 설계까지 함께 익히는 구조이다.

1. 기본 요구사항

1-1. 유저 인증 기능

  • Prisma를 이용해 User 모델 정의
    → 필드: id, email, nickname, imageURL, password, createdAt, updatedAt

  • 회원가입 구현
    → email, nickname, password를 입력받아 저장
    → 비밀번호는 bcrypt로 해싱 후 저장

  • 로그인 구현
    → 로그인 성공 시 Access Token 발급

1-2. 상품 기능 (인가 처리)

  • 로그인한 유저만 상품 등록 가능

  • 상품을 등록한 본인만 수정/삭제 가능 (ownership 체크 적용)

1-3. 게시글 기능 (인가 처리)

  • 로그인한 유저만 게시글 등록 가능

  • 게시글 역시 작성자만 수정/삭제 가능

1-4. 댓글 기능 (인가 처리)

  • 로그인한 유저만 댓글 작성 가능

  • 댓글을 작성한 유저만 본인 댓글 수정/삭제 가능

  • 상품/게시글 각각에 댓글을 남길 수 있음

1-5. 유저 정보 관련 기능

  • 로그인한 유저가 자신의 정보를 조회/수정 가능

  • 비밀번호도 별도 API로 변경 가능 (해싱 처리)

  • 내가 등록한 상품 / 게시글 목록도 따로 조회 가능

  • 비밀번호는 절대 응답에 포함되지 않도록 처리

2. 심화 요구사항

2-1. Refresh Token 구현

  • Access Token 만료를 대비해, Refresh Token을 사용해 Access Token 재발급 기능 구현

  • Refresh Token은 httpOnly 쿠키로 보관 → 보안상 JS 접근이 불가능하게 처리

2-2. 좋아요 기능

  • 유저는 상품/게시글에 좋아요 & 좋아요 취소 가능

  • 해당 상품/게시글 조회 시
    → isLiked: true 같이 내가 좋아요 눌렀는지 확인 가능

  • 내가 좋아요 누른 상품/게시글 목록도 별도 API로 조회 가능

3. 주요 구현 코드 예시

3-1. Prisma 모델 (schema.prisma)

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  nickname  String   @unique
  image     String?
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  products      Product[]
  articles      Article[]
  comments      Comment[]
  productLikes  ProductLike[]
  articleLikes  ArticleLike[]
  refreshTokens RefreshToken[]
}

// ...Product, Article, Comment, ProductLike, ArticleLike, RefreshToken 모델 정의

3-2. 인증 라우터 (auth.routes.js)

  • 회원가입

  • 로그인 (JWT, RefreshToken 발급 및 httpOnly 쿠키 저장)

  • AccessToken 재발급

  • 로그아웃 (RefreshToken 폐기 및 쿠키 삭제)

router.post('/login', checkCredentials, async (req, res, next) => {
  // ...중략
  res.cookie('refreshToken', refreshToken, {
    httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict',
    maxAge: 1000 * 60 * 60 * 24 * 7,
  });
  return res.json({ accessToken });
});

3-3. 유저 정보 관련 (user.routes.js)

// 내 정보 조회
router.get('/me', authenticate, async (req, res, next) => {
  const user = await prisma.user.findUnique({
    where: { id: req.user.id },
    select: { id: true, email: true, nickname: true, image: true, createdAt: true, updatedAt: true }
  });
  res.json(user);
});

// 내 정보 수정, 비밀번호 변경, 내 상품/게시글/댓글/좋아요 목록 조회 등도 포함

3-4. 상품/게시글 좋아요

// 상품 좋아요
router.post('/:id/like', authenticate, async (req, res) => {
  await prisma.productLike.upsert({
    where: { userId_productId: { userId: req.user.id, productId: Number(req.params.id) } },
    create: { userId: req.user.id, productId: Number(req.params.id) },
    update: {},
  });
  res.status(204).end();
});

// 내가 좋아요한 상품 목록
router.get('/me/liked-products', authenticate, async (req, res) => {
  const products = await prisma.product.findMany({
    where: { likes: { some: { userId: req.user.id } } }
  });
  res.json(products);
});

4. 트러블슈팅 & 노하우

  • Prisma select 필드 에러 발생

  • 모델 필드명(image, imageUrl, imageURL 등) 오타, 대소문자 불일치로 에러 다수

  • 해결: schema.prisma 필드명과 코드의 select 필드명을 100% 일치시켜야 한다.

  • 마이그레이션/DB 구조 꼬임

  • 필드 추가/삭제 후 migrate dev/reset, generate, seed 순서 꼼꼼히 확인할 것

  • 쿠키 방식 Refresh Token

  • httpOnly, secure, sameSite 등 쿠키 옵션 세팅하기

  • Postman에서는 Cookies 탭 활용하기

  • 이미지 업로드

  • multer 적용, uploads 폴더 자동 생성

  • git 충돌/브랜치 병합 이슈

  • fetch/pull/merge/rebase 사용법 익힘

  • 충돌 났을 때 커밋 되돌리고 새 브랜치 만들어 안전하게 작업하기

5. 느낀 점 / 회고

  • JWT + RefreshToken의 실전 적용법을 직접 구현하며 인증 인가의 흐름을 명확히 이해할 수 있었다.

  • Prisma의 관계형 설계와 select의 엄격함을 경험하며 타입 안전성을 체감하였다.

  • Postman을 통한 API 검증, 쿠키 활용, 에러 상황별 대응법 등 실전 노하우가 쌓였다.

  • 구조적인 서비스/컨트롤러/미들웨어 분리 습관이 생겼다.

  • 에러 메시지 꼼꼼히 읽기, 공식 문서 적극 활용하기가 가장 빠른 해결책임을 느낀다.

profile
node.js 백앤드 개발자가 되기 위한 Study Vlog

0개의 댓글