nestjs, prisma, postgres ,graphql + nextjs v13 으로 게시판 만들기(1) - 회원가입 로그인(1)

junny·2023년 4월 19일
0

nsetjsNextjs

목록 보기
1/1
post-thumbnail

순서
1편 회원가입 로그인(1) -> 세팅
2편 회원가입 로그인(2) -> 로직구현
3편 로그인 에러핸들링 및 테스트 코드 작성
4편 게시판 로직 및 게시판 에러 핸들링 및 테스트 코드 작성
5편 nextjs 작업 -> 더 길어질 수 있음

react v18, nextjs v13으로 점차 진화하면서,
서버 컴포넌트와 클라이언트 컴포넌트 조합으로 web site 만드는게 가능해졌다.
원래 필자는 graphql이 그렇게 필요한지 의문이였지만
프론트가 이렇게 달라지면 말이 달라진다.

이제 페이지 단위로 렌더링이 달라지는게 아니라 컴포넌트 단위로 렌러링을 할 수 있다는 엄청난 변화이다.
그럼 데이터 페칭도 굉장히 세부적으로 할 것 같은데, 기존 restful api로는 backend나 front나 서로 귀찮은 일이 될 수 있을 것 같다.

while(true){
프론트 사람: "저 이 api도 만들어 주실 수 있어요?"
백엔드 사람: "네"
if(프로젝트 끝) break;
}

그래서 이제 진짜 graphql을 해야만 할 것 같다는 예측을 해보면서

그럼 개발자 답게 만들면서 배워보자!

nestjs + prisma + graphql + postgres setting

nestjs에 postgres, prisma를 연동하자

(필자는 yarn을 좋아해서 yarn으로 세팅했습니다.)

nest cli로 새로운 프로젝트를 만들고

postgres 연동

docker-compose를 이용해 postgres를 연동
postgres를 연동 최상위에 docker-compose.yml 파일을 아래와 같이 만들자
필자는 5435 포트에 postgres 서버열기 위해 아래와 같이 설정했다.
🥕 아직도 저는 volumes를 잘 이해 하지 못했습니다..혹시라도 잘아시는 분이 이 글을 보신다면 답글로 좀 알려주시면 감사하겠습니다.

version: '3.8'
services:

  postgres:
    image: postgres:15.2
    restart: always
    env_file:
      - ./.env
    volumes:
      - ./db_data:/usr/local/postgresql/data
    ports:
      - '5435:5432'

.env 파일을 만들고 .gitignore에 .env를 추가한 다음
.env 파일에

DATABASE_URL=`postgres://{${db username}:${db password}@${host}:${port 번호}/${db 명}`

예시
DATABASE_URL="postgres://junnyletsgo:1234@localhost:5435/prismaGraphql"

이렇게 세팅을 해줬으면

docker-compose up -d

로 도커를 백그라운드로 실행시켜주자
도커로 가보면 잘 실행되는지 확인해 볼 수 있습니다.

mac을 사용한다면 강력추천하는 postico로 아니면 pdAdmin으로 서버를 세팅해주자.
이때 db안에 아무것도 안만들면 나는 에러는 추후 prisma를 세팅하면 해결되니 일단 아래로!

prisma 세팅

prisma를 nestjs에 설치

yarn add prisma

prisma 초기 세팅을 해보자

npx prisma init

를 실행하면 최상위 폴더에 prisma폴더가 생긴다.
prisma/schema.prisma 파일로 들어가면

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

이렇게 세팅되어 있을 것 같은데 "postgresql"가 아니라면 "postgresql"로 바꿔주자

게시판을 아주 간단히 만들 계획이기 때문에

user는 email, passwaord
post는 title, content, createAt, updateAt 정도로 구성하려고 한다.

model User {
  id       Int    @id @default(autoincrement())
  email    String @unique
  password String
  post     Post[]
}

model Post {
  id       Int      @id @default(autoincrement())
  title    String
  content  String
  createAt DateTime
  updateAt DateTime
  User     User     @relation(fields: [userId], references: [id])
  userId   Int
}

이렇게 모델을 만들고

npx prisma migrate dev --name "init"

아래 명령어를 실행
그러면 prisma/migrations/날짜_init 폴더 안에 migration.sql이 생성되면서
postico나 pgadmin에 가보면 db가 생겨 있어야만 한다. 아니면 오류니 확인!!

지금 부터는 prisma module과 service를 만들어서 prisma 연결에 관한 설정도 하고 depency injection을 통해서 prisma를 사용해보자

nest g mo prisma //모듈생성
nest g s prisma // 서비스 생성

prisma/prisma.service.ts


import { INestApplication, Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {
  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }
}

prisma/prisma.module.ts

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

이렇게 세팅을 해주자

이제 graphql 세팅을해보자

들어가기 전에
Graphql nestjs에서 사용하는것에는 두가지 방식이 있다
1. code first
2. schema first

1번 방식은 js 2번 방식은 ts 같다고 보면 된다. 자세한건 공식문서에서(https://docs.nestjs.com/graphql/quick-start) 확인!
간단히 생각해봐도 먼저 타입을 지정하고 코드를 치는게 안전하고 편하다(조금 더 심도 있게 즐기고 싶다면 https://code-masterjung.tistory.com/22 이 글을 읽어보는 걸 추천!)
그러니 여기에서는 2번 방식을 사용해서 코딩을 하겠다!

yarn add @nestjs/graphql @nestjs/apollo @apollo/server graphql ts-morph

위의 명령어를 실행하자
갑자기 아폴로라고 생각한다면 나중에 서버를 연동하고 localhost:포트번호/graphql 을 치면 graphql playground를 즐길 수 있다.
디버깅에도 용이하니까 같이 설치를 하자

그럼 app.module.ts에서

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path';

@Module({
  imports: [
    PrismaModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      typePaths: ['./**/*.graphql'],
      definitions: {
        path: join(process.cwd(), 'src/graphql.ts'),
        outputAs: 'class',
      },
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

이렇게 설정을 해주면 scr/graphql.ts에 .graphql 파일들을 class 타입으로 바꿔준다.

지금 yarn start:run dev 을 실행시킨 당신

쿼리타입을 지정하라는 에러가 났을 것이다.
이건 당연한거니 쭉 진행을 하자

nest g resource

를 실행하면
이름,
개발방식 선택(rest api, graphql code first, graph ql schema first 등)
crud 엔드 포인트를 만들어 줄껀지
를 선택하라고 나오는데 필자는 위처럼 선택을 했다.

그럼 user폴더가 생기면서 그 안에
module, resolver, service, user.graphql 파일이 생긴다.
위의 쿼리 타입에러를 해결하기 위해서
user.graphql로 가서

type Query {
  user: String
}

이렇게 바꿔주고
resolver 파일로 가서


 @Query('user')
  함수명 아무거나() {
    return "";
  }

이렇게 바꿔주면 에러가 해결이 된다.

최소 하나의 쿼리가 없으면 에러를 낸다. mutation만 있어도 동일! query는 하나 만들어 줘야한다.

회원가입!!... 세팅!

개발방식은
1. jwt token 방식으로 인증을 구현한다.
2. 비밀번호는 bcrypt를 통해서 hash 한다.

인증관련된것을 auth에 모아서 한번에 처리하자

nest g mo auth // 모듈생성
nest g s auth // 서비스 생성

그리고 인증 관련된 라이브러리를 설치

yarn add @nestjs/jwt @nestjs/passport passport passport-jwt @types/bcrypt bcrypt @nestjs/config
yarn add -D @types/passport-jwt

이렇게 설치를 했으면
app.module.ts에 환경변수를 쉽게 처리하기 위해서

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { UserModule } from './user/user.module';
import { AuthService } from './auth/auth.service';
import { join } from 'path';
import { ConfigModule } from '@nestjs/config'; //<- 추가

@Module({
  imports: [
    PrismaModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      typePaths: ['./**/*.graphql'],
      definitions: {
        path: join(process.cwd(), 'src/graphql.ts'),
        outputAs: 'class',
      },
    }),
     UserModule,
    AuthModule,
     ConfigModule.forRoot(),// <- 추가 (나중에 joi validation 예정)
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

위처럼 했다면
.env에

JWT_SECRET_KEY= JWT_SECRET_KEY generateor를 이용한 key를 넣어주세요

auth.module.ts에 세팅

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { PrismaModule } from 'src/prisma/prisma.module';
import { PrismaService } from 'src/prisma/prisma.service';
import { UserRepository } from 'src/user/user.repository';
import { AuthService } from './auth.service';

@Module({
  imports: [
    PassportModule.register({
      defaultStrategy: 'jwt',
      session: false,
    }),
    JwtModule.register({
      global: true,
      secret: process.env.JWT_SECRET_KEY,
      signOptions: {
        expiresIn: '24h',
      },
    }),
    PrismaModule,
  ],
  providers: [AuthService, UserRepository, PrismaService],
  exports: [AuthService],
})
export class AuthModule {}

이제 진짜 코드칠 준비가 끝났다.

설정관련해서는 다음편에서 설명할 예정입니다.!!!!!!

세팅만해도 빡쌨지만 다음부턴 즐거운 로직 구현 시간이다.
궁금한게 있으면 언제든 환영!!
물어보세요!

https://github.com/jjuny0113/prisma_graphql

위의 깃에서 작업하고 있습니다.

profile
오히려 좋아!

0개의 댓글