How to setup Sequelize migration in a NestJS Project 참고했다.
기존 프로젝트 셋팅에서 추가 or 수정된 부분
마이그레이션 명령어로 예제 테이블 생성까지 확인
1차로 진행했던 스키마는 테마변경을 할 때, 해당 색깔을 찾아서 다 바꿔줘야하는 번거로움이 있었다.
그래서 색깔이 아닌 fk 만 바꿀수 있게 다시 설계하였다.
그리고 기존에 조인테이블을 user_page 로 만들어서
highlightId 로 입출력값을 다룰 때 헷갈렸어서 테이블 이름도 변경하였다.
API 문서도 다시 작성했다.
기존에는 model/index.js 에 아래처럼 설정했었다.
const { Theme, User, User_Page, Page } = sequelize.models;
Theme.hasMany(User);
User.belongsTo(Theme);
User_Page.belongsTo(User);
User.hasMany(User_Page);
User_Page.belongsTo(Page);
Page.hasMany(User_Page);
nestjs + typescript + sequelize 는 달랐다.
src/highlight/highlight.model.ts 에는 아래처럼 작성하였다.
import {
Column,
Model,
Table,
ForeignKey,
BelongsTo,
} from 'sequelize-typescript';
import { Page } from 'src/page/page.model';
import { Theme } from 'src/theme/theme.model';
import { User } from 'src/user/user.model';
@Table
export class Highlight extends Model {
@ForeignKey(() => User)
userId: number;
@ForeignKey(() => Page)
pageId: number;
@Column
text: string;
@BelongsTo(() => Theme)
themeData: number;
@ForeignKey(() => Theme)
colorId: number;
}
이 파일에서는 누가 어떤 역할을 하는지 분명하게 알 수 있다.
@Module({
imports: [
SequelizeModule.forRoot({
dialect: 'mysql',
host: 'localhost',
username: 'root',
password: null,
database: 'liner_nest',
models: [User, Theme, Page, Highlight],
}),
UserModule,
ThemeModule,
PageModule,
HighlightModule,
],
controllers: [AppController],
providers: [AppService],
})
import {} from '' 부분이 너무 많아서 생략했다.
아무튼 imports 부분을 통해 requelize 를 연결했다.
controllers 에서는 express 의 라우팅 역할을 하고
providers 에서는 실제 서비스로직이 있다.
리팩토링 전에는 전부 body 로 데이터를 받았다.
이번에는 body 는 물론 query, param 모두 사용해봤다.
기존에는 내용마다 각각 파일을 만들어서 조금 복잡하다고 생각했다.
이번에는 app.controller.ts 파일에 모든 라우팅을 정리했다.
@로 라우팅이 나눠지는데 한 눈에 보여서 직관적이라고 생각했다.
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
sayHi() {
return this.appService.getHello();
}
@Post()
createHighlight(
@Body() createHighlightDto: CreateHighlightDto,
): Promise<resData> {
return this.appService.create(createHighlightDto);
}
@Patch()
updateHighlight(
@Body() updateHighlightDto: UpdateHighlightDto,
): Promise<resData> {
return this.appService.update(updateHighlightDto);
}
// http://localhost:3000/highlights?userId=1&pageUrl=naver.com&pageId=123 이면 123 우선
@Get('highlights')
readHighlights(
@Query('userId') userId: number,
@Query('pageUrl') pageUrl?: string,
@Query('pageId') pageId?: number,
) {
return this.appService.readhighlights(userId, pageId, pageUrl);
}
@Get(':userId')
readAll(@Param('userId') userId: number) {
return this.appService.readAll(userId);
}
@Delete(':userId/:highlightId')
deleteHighlight(
@Param('userId') userId: number,
@Param('highlightId') highlightId: number,
) {
return this.appService.delete(userId, highlightId);
}
@Put(':userId/:themeId')
changeTheme(
@Param('userId') userId: number,
@Param('themeId') themeId: number,
) {
return this.appService.changeTheme(userId, themeId);
}
}
내가 제일 시간을 많이 쏟은 파일이다. (당연)
이번에는 서비스 관련 로직을 하나의 파일에 다 넣어보았다.
총 333줄의 코드가 나왔지만, 각각의 기능은 길지 않았다.
예전에는 기능마다 파일만들어서 쪼갰는데, 굉장히 귀찮았다.
내용이 적으니까 가능한 것 같다.
아마 실무를 하게 될 때는 쪼개는 게 맞다고 생각한다.
interface resData {
highlightId: number;
userId: number;
pageId: number;
colorHex: string;
text: string;
}
이렇게 결과 인터페이스도 만들어 보았다.
각 기능의 로직에 관해 코드리뷰 받고싶다 ㅠ
더 나은 방법이 있는지도 알고싶고, 스키마는 적절한지도 평가받고 싶다.
이번에는 예외처리가 되는 부분이 없게 심혈을 기울였다.
const { userId, pageUrl, colorHex, text } = data;
// 유저확인
const checkUser = await User.findOne({
where: { id: userId },
});
if (!checkUser) {
throw new NotFoundException(`User with id: ${userId} not found`);
}
// Url 확인
const checkUrl = await Page.findOrCreate({
where: { pageUrl: pageUrl },
});
const pageId = checkUrl[0].getDataValue('id');
이렇게 파라미터에 대해서 하나하나 확인하는 과정은 물론
필수 파라미터가 들어오지 않는 부분에 대해서도 처리를 해줬다.
이렇게 세세하게 처리를 하니, 테스트에서 정말 머리가 지끈거렸다.
각잡고 대부분의 경우의 수 따져가면서 코드 구현했더니.
테스트 진행할 때, 진짜 토나올뻔 했다.
그런데 이렇게 하는 게 맞는 거겠지?
예전 프로젝트 진행할 때는 테스트코드도 짜지않고
에러처리도 세세하게 진행하지 않았다.
그 당시 내 코드가 얼마나 부족했는지 이번 리팩토링을 하면서 느꼈다.
typescript sequelize / create error
데이터베이스에 새로운 데이터를 넣으려고 하자 에러가 발생했다.
-> 예전 문법을 최신 문법으로 변경해서 해결했다.
nestJS / 'cannot find module' when test
테스트를 돌리는데 import 사용했던 부분이 다 없다고 에러가 떴다.
-> package.json 에서 jest 설정을 변경해서 해결했다.
sequelize/ 로 받아온 데이터 키, 값 지우기
데이터베이스에서 가져온 데이터를 추가나 변경은 가능했지만 삭제가 되지 않았다.
-> 데이터를 json 으로 변경한 후에 필요없는 키, 값을 삭제하고 내보냈다.
sequelize /유닛테스트 진행시 데이터베이스 연결
테스트 진행할 때 findOne 같은 메소드가 실행이 되지 않았다.
-> 테스트파일에도 데이터베이스 연결을 해줘야 했다.
nestJS 사용하면서 미완성된 코드에 빨간줄이 계속 떠있으니까 짜증스러웠다.
하지만 해당 함수를 다른 곳에서 사용할 때, 타입과 어떤 파라미터를 써야하는지 알려줘서 편리했다.
구현을 하면서 ts 를 더 깊게 공부해야겠다고 생각했다.
아직 인터페이스나 타입설정에 관해 능숙하지 못해서, 리턴값에 대한 타입정의를 빼먹은 부분이 많다.
공부를 하면 할 수록 내가 부족하다는 걸 더 느끼게 된다.
잘 해내고 싶다.