Prisma를 이용해 User 모델 정의
→ 필드: id, email, nickname, imageURL, password, createdAt, updatedAt
회원가입 구현
→ email, nickname, password를 입력받아 저장
→ 비밀번호는 bcrypt로 해싱 후 저장
로그인 구현
→ 로그인 성공 시 Access Token 발급
로그인한 유저만 상품 등록 가능
상품을 등록한 본인만 수정/삭제 가능 (ownership 체크 적용)
로그인한 유저만 게시글 등록 가능
게시글 역시 작성자만 수정/삭제 가능
로그인한 유저만 댓글 작성 가능
댓글을 작성한 유저만 본인 댓글 수정/삭제 가능
상품/게시글 각각에 댓글을 남길 수 있음
로그인한 유저가 자신의 정보를 조회/수정 가능
비밀번호도 별도 API로 변경 가능 (해싱 처리)
내가 등록한 상품 / 게시글 목록도 따로 조회 가능
비밀번호는 절대 응답에 포함되지 않도록 처리
Access Token 만료를 대비해, Refresh Token을 사용해 Access Token 재발급 기능 구현
Refresh Token은 httpOnly 쿠키로 보관 → 보안상 JS 접근이 불가능하게 처리
유저는 상품/게시글에 좋아요 & 좋아요 취소 가능
해당 상품/게시글 조회 시
→ isLiked: true 같이 내가 좋아요 눌렀는지 확인 가능
내가 좋아요 누른 상품/게시글 목록도 별도 API로 조회 가능
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 모델 정의
회원가입
로그인 (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 });
});
// 내 정보 조회
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);
});
// 내 정보 수정, 비밀번호 변경, 내 상품/게시글/댓글/좋아요 목록 조회 등도 포함
// 상품 좋아요
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);
});
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 사용법 익힘
JWT + RefreshToken의 실전 적용법을 직접 구현하며 인증 인가의 흐름을 명확히 이해할 수 있었다.
Prisma의 관계형 설계와 select의 엄격함을 경험하며 타입 안전성을 체감하였다.
Postman을 통한 API 검증, 쿠키 활용, 에러 상황별 대응법 등 실전 노하우가 쌓였다.
구조적인 서비스/컨트롤러/미들웨어 분리 습관이 생겼다.
에러 메시지 꼼꼼히 읽기, 공식 문서 적극 활용하기가 가장 빠른 해결책임을 느낀다.
