작업 배경

우리 팀의 서버 개발 히스토리와 상황은 다음과 같다.

  • 최초 express.js로 개발 -> Nest.js 도입하여 express는 라우트 연결하여 유지, 새 기능은 Nest.js로 개발
    • express.js: shecma-first(graphQL 스키마를 직접정의), Seqeulize
    • Nest.js: code-first(graphQL 스키마를 코드로 정의한 뒤 생성), prisma
  • Apollo GraphQL 기반
  • 이전에 개발된 express 의 공고 스크랩 API
  • 최근에 개발된 Nest.js 의 관심기업 설정 API

작업

전략

요구사항은 공고를 스크랩할 때 해당 공고의 기업에 관심설정을 하도록 하는 것이다. 아래에 채택되지 않은 방안과 제약을 정리해보았다.

  • 클라이언트에서 두 API를 각각 호출: 구버전 앱에서 호환되지 않음
  • express 로직에서 Nest.js API를 호출: 한 서버 프로세스에서 불필요한 네트워크 호출
  • express 코드를 모두 Nest.js로 작성: 공고달력, 팔로우 등 많은 기능과 얽혀있기에 검증이 어려워 서비스에 영향을 줄 수 있음

결론적으로는 위 두 방안이 아닌 Nest.js 기반의 엔드포인트와 인풋/아웃풋 타입을 정의하고, 내부에서 express 서비스 함수를 호출하고도록 하였다. 이 전략은 다음과 같은 장점과 한계를 가진다.

장점

  • Apollo express 서버는 요청 context 에 서비스 클래스를 포함한다는 점을 활용하여 쉽게 구현할 수 있다
  • 코드 기반을 Nest.js 로 구성하여 Nest.js의 가드, 미들웨어, 인터셉트와 같은 인프라 기능을 활용할 수 있다
  • GraphQL 스키마를 코드로 관리하여 스키마 관리가 용이

단점

  • express 코드에서는 sequelize 를 사용하고 Nest.js 코드에서 prisma 를 사용하고 있기에, 트랜잭션으로 처리하기 위하여 별도의 처리가 필요하다.

참고한 글

코드 샘플

  • 기존 공고 스크랩 API
// src/resolvers/mutation.js
const resolvers = {
  Mutation: {
      activityScrap: async (_, { input }, context) => {
        // 1. Apollo Context에서 서비스 추출
        const { userScrapService } = context;

        // 2. 공고 스크랩
        const result = await userScrapService.updateUserScrap(input);

        return result;
      }
    }
 }
  • 새 공고 스크랩 API
// src/scraps/resolvers/user-scraps.resolver.ts
@Resolver(() => UserScrapDto)
export class UserScrapsResolver {
  constructor(private scrapsService: ScrapsService) {}

  @Mutation(() => ScrapPayloadDto, { nullable: true })
  async activityScrap(
    @Context() context: RequestContext,
    @Args('input') input: ScrapInputDto,
  ) {
    return await this.scrapsService.updateActivityScrap(context, input)
  }
}

// src/scraps/services/user-scraps.service.ts
public async updateActivityScrap(
    context: RequestContext,
    input: ScrapUpdateInputDto,
  ): Promise<ActivityScrapUpdatePayloadDto | null> {
    // express 서비스 함수 
    const { userScrapService } = context

    const result = await userScrapService.updateUserScrap(input);

    // Nest.js 서비스 함수
    await this.companyInterestService.setCompanyInterestedIn(
      context,
      compnayId
    );

    return result
  }

향후 계획

순수 비즈니스 로직 또한 모듈 단위, 함수 단위로 나누어 NestJS Service로 하나씩 추출할 예정이다.

 

 

+ Recent posts