프로그래밍/NestJS

nestjs/typeorm 0.3.x 적용 / Custom Repository가 안 된다고?

카카수(kakasoo) 2022. 6. 2. 23:49
반응형

Sequelize가 ORM이냐 아니냐를 가지고 많은 얘기가 오간다. 설명에도 대놓고 ORM이라고 말하고 있지만, Java 에서 넘어온 개발자들에게 Sequelize는 어딘가 엉성한 친구였을 것이고, TypeScript가 나온 이상, JavaScript의 고유성이라고 변명하기는 힘들어 졌을 것 같다. 다만, 그렇다고 해도 TypeORM 역시 엉성한 부분이 많아, 절대적이라고 말하지는 못했을 것 같다. 양쪽 다 부족함을, 취향의 차이로 구분해야 했다. 하지만 TypeORM ^0.3.x 부터는 많은 게 달라졌다.

 

일단 비교를 위해 기존의 코드를 하나 보자.

 

 

이전에 짰던 코드 중 일부를 가져왔다. 이 코드는 TypeORM의 QueryBulder 를 이용해서 만든 코드다. withDeleted 를 true로 하여 삭제된 데이터를 함께 조회하면서, leftJoin을 여러 차례 반복하고, 2차례의 where문을 사용한다. 번거로운 여기에 혹여 select 문들이 들어가게 됐다면 코드는 더욱 복잡해졌을 것이다.

 

사실 의미로만 봤을 때는, "상품에, 썸네일과 상세 이미지, 카테고리, 옵션들을 join해서 가져와줘." 라는 의미인데도, 지나치게 어렵게 짜야만 한다.

 

 

이번에 업데이트된 TypeORM에서는 코드를 이렇게 수정할 수 있다. 기존에는 복잡했던 QueryBuilder를 Repository를 통해서도 얼추 구현할 수 있게 되었다. relations와 where문은, 기존 방식에서는 단순한 string을 key mapping 해주던 방식에서, 정말 TypeScript에 맞게 바뀌었다. 에러가 날 거라고 예상되면 코드를 작성하는 단계에서도 알아챌 수 있게끔. 하지만 이 상태에서도 아직 문제가 있다.

 

1. innerJoin을 할 수 없다는 점.

2. option을 join할 때처럼 join에 condition을 걸 수 없다는 점.

3. take에 1을 값으로 줘서 1개를 받은 다음, 구조 분해 할당으로 1개를 꺼내야 하는 점, findOne이나 getOne같은 기능이 없다는 점.

 

사실 이런 부분들은 서버에서 처리하면 된다. 코드를 짜는 데 더 쉬워진 만큼, DB보다는 서버에 초점을 맞추는 게 더 나아졌을 것이다. 이전에는 사실, 방법이 없거니와, 어차피 복잡한 로직을 짜야 한다면 그냥 기왕 할 거 최대한 잘 만들자는 쪽으로 설계했던 것을, 이제는 조금 더 생산성을 추구할 수 있게 된 것 같다.

 

그런데, Custom Repository가 주입이 안 된다고?

 이로 인해서 업데이트를 하자마자 내 코드들이, 중복을 줄이고자 customize한 Repository를 찾지 못해 모조리 터져 나갔다. 이를 수정하고자 하니 글쎄, @EntityRepository()가 이제 TypeORM module에서 사라졌다고 한다. 안타깝게도 현재로서는 TypeORM을 도와줄만한 전용 decorator들이 없다. NestJS를 함께 쓰는 사람들에게는 끔찍한 일이 아닐 수 없는데, 매번 InjectRepository를 사용할 때마다 적절한 토큰 값을 찾아서 넣어줘야 하기 때문이다. 내 Repository는 각 유저의 성격에 따라 서버를 여러 개로 나누고, 각 서버 별로 Repository를 가지게 하는 방식이었는데, 당연히 Repository의 수는 무수히 ㅏ많았고 수정할 것도 산더미처럼 쌓였다. 이를 더 쉽게 수정할 방법이 없을까?

 

찾던 중 좋은 아이디어를 발견하여 공유한다.

 

 

이제 EntityRepository 대신에 CustomRepository를 사용해주면 된다. CustomRepository는 SetMetadata를 이용해서 특정한 token 값으로 entity를 저장하는 로직이 된다. EntityRepository와 크게 다를 바 없다. 이렇게 주입해주는 건 Role이나 Class-validator를 직접 custom 해서 다뤄봤다면 이미 익숙할  코드다.

 

 

다음은 CustomTypeOrmModule의 정의다. 이전의 TypeOrmModule을 사용하지 않는 것은 아니다. 이 경우에는 Entity들을 이용해 코드를 짤 때만 사용한다. 자세한 건 아래 모듈 코드를 보면 이해가 갈 것이다.

 

 

Entity를 사용할 때는 TypeOrmModule로, Repository를 사용할 때는 CustomTypeOrmModule로 구분하여 사용한다. 이렇게 하면 Custom한 Repository의 경우, 해당 metadata token을 이용해서 값들을 찾은 다음, 그 값들 중 일치하는 것들을 추후 삽입하는 방식이다. 자세한 원리는 Reflect의 get, setMetadata를 참고하자.

 

1. CustomRepository와 CustomTypeOrmModule 코드를 작성한다.

2. EntityRepository는 이제 사용 불가하므로, CustomRepository 데코레이터로 변경한다.

3. CustomTypeOrmModule.forFeature()를 이용해서 Repository를 Module에 등록해준다.

4. 매번 주입하는 방식이 아니라 데코레이터와 Module import를 이용해서 이 문제를 해결하는 데에 성공

반응형