액티비티

[위코드 X 원티드 ] 프리온보딩 백엔드 - 카닥 기업과제 후기

yjs3819 2021. 11. 29. 12:20
728x90

나혼자.. 한다

이번 과제는 개인과제로, 나 혼자 개발했다.
팀원과 함께 고민하며 소통하며 개발하던게 정말 재밌었는데, 너무 아쉬웠다.

그렇다고 대충한건 아니다!

나 스스로 생각하고, 내 스스로 기능을 판단하여 구현하였다.
데이터베이스 설계부터 배포 까지..

이제 개발하면서 느낀점, 고민들 혹은 해결법 등의 회고를 적어보겠다.

개발 고민

요구사항이 정확하지 않는데 나 스스로 판단하는 건가?

내가 볼땐, 요구사항이 분명치 않은 부분이 몇개 있었다.

  1. 타이어 저장은 인증 처리가 필요한데, 다른 유저의 타이어도 저장할 수 있다??

이 부분이 너무 헷갈렸다.
아니면 직원임을 인증해서 사용자의 타이어 정보를 저장하는건가? 라는 생각도 하였다.

생각해보니, '모두 사용자일 거라는 것은 착각이다'라는 생각이 들었다.

그래서 스스로 상황을 가정했다.
타이어 저장은 직원이 하는 거니, 직원의 인증을 요구하는 것일 거야

  1. 타이어 정보를 저장하는 api라는데, 세부 설명엔 자동차 정보를 저장??

제목과, 세부설명이 달라서 처음엔 당황했다.
그러나 고민해보니 타이어 정보를 저장하는것이 주 목적이나, 자동차 정보도 저장해야한다고 생각했다.

그래서 난 데이터베이스를 설계할 때, 자동차 정보도 저장했다.(이건 요구사항 때문에 한건 아니고 여러 이유가 있다. 그건 아래서 살펴보자!)

결론은!
요구사항을 내가 스스로 이해하고 판단하는 것도 능력이란 생각이 들었다. 구현하라는 주된 요구사항을 정확히 판단하고, 그외의 부가적인 상황들은 스스로 판단하여 구현하는것 이것 또한 개발자로써 중요한 능력이라 생각했다.

내가 가정한 상황이 적절할까? 그렇지 않으면??

나는 한 자동차 (trim)에 대한 타이어는 절대 변경되지 않는다고 가정했다.

왜?? 자동차 타이어 종류 바꿀수 있잖아????

왜냐면
유저의 타이어가 바뀐다면, 수정하는 api가 존재해야 한다 생각했다.
즉, 수정하는 api가 없다면 저장하는 로직에서 자동차의 타이어가 수정되면 안된다고 생각했다.

그렇기에 수정하는 api가 없으니, 일단 저장 api 입장에선 변경을 판단하면 안된다 생각했다.

그래도 내가 판단한 가정이 맞을까 고민을 많이 했다.

그래도 맞다 생각하고 밀어 붙였다!! 고민만 하면 아무것도 진전이 없다 생각했다. 일단 개발해보고! 개발하다 이상하면 바꾸기로!

또한, 위처럼 생각한다면 타이어를 조회할시 한 유저가 같은 trimId의 차를 여러개 가지고 있을 수 있다 생각했다. 같은 trimId의 차를 여러개 가지고 있다면 같은 타이어 정보만을 응답할 것이다. 이건 어쩔수 없는 문제라 생각했다. (수정하는 로직은 수정하는 로직에서 따로 만들어야 한다고 생각했다.)

저장하는 로직은 단순히 저장만하고, 수정하는 로직이 필요하다면 해당 로직에서 수정해야한다 생각했다.

물론, 내가한 가정이 적절하지 않을 수가 있다. 지금 블로그에 포스팅하면서 느낀 점인데,trimId라는게 차종이 아니라 단순히 차를 식별하는 number가 아닐까? 라는 생각이 든다..(내가 개발한 것이 잘한것인가? 의문이든다..., trimId가 차 하나하나를 식별하는 id라면,, 하나의 trimId의 차를 여러명이 동시에 가질수는 없을터.. 차의 소유주는 한명이기에..)

혼자서 개발하다 보니, 이러한 고민들이 해소가 되지 않았다. 여러명과 함께 소통하며 고민을 얘기하면 해결될 수 있을 것 같다.

데이터베이스 설계와 변경..

처음에는 한명의 유저가 여러 trimId를 가지고 있을 수 있으니 단순히 유저 : trim = 일 대 다 관계를 생각했다.

그러나 하나의 trimId를 여러명이 가질수 도 있다!! 라는 생각이 들었다.(trimId 를 '차 종류'라고 생각했기에)

물론 일대다 관계하나로 표현할 수 있지만, trim에는 굉장한 중복이 생길거라 염려되었다.

그래서 다대다 관계로 풀기로 결정했다.

그런데, 문제점은 다대다 관계에서의 연결테이블은 두개의 외래키 (user_id, trim_id)를 pk로 하기에, 한 유저가 같은 trim을 여러개 가질 경우를 식별하지 못하는 단점이 있었다.

그래서 난, @ManyToMany관계를 이용해서 연결테이블을 만드는게 아닌, @OneToMany, @ManyToOne 을 이용해 직접 연결테이블을 만들고, pk도 하나 직접 만들었다. 그렇다면 한 유저가 동일한 trimId를 여러개 가질 수 있고, row하나 하나를 유일하게 pk로 식별 할 수 있다.


(이렇게!)

trimId를 차종이라고 생각하기에 이러한 설계를 하였다. 만약.. trimId가 차종이 아니라 차 하나하나를 식별할 수 있는 id라면..? 하나의 trimId를 여러명이 동시에 가질수 없다. 즉, 단순히 유저 : trim = 일 대 다 관계만 적용된다 생각한다.

테스팅

이번엔 꼭 유닛테스트를 해보고 싶었다.
그래서, 타이어 저장 유닛테스트를 해보았다.

타이어 저장은 createQueryRunner를 통한, 트랜잭션 처리가 되어있었기에, 이를 어떻게 모킹시킬까 고민이 굉장히 많았다.

  const queryRunner = {
    manager: {},
  } as QueryRunner;

  class ConnectionMock {
    createQueryRunner(mode?: 'master' | 'slave'): QueryRunner {
      return queryRunner;
    }
  }

이렇게 가짜 커넥션과, 가짜 queryRunner를 정의하고

  beforeEach(async () => {
    queryRunner.connect = jest.fn();
    queryRunner.release = jest.fn();
    queryRunner.startTransaction = jest.fn();
    queryRunner.commitTransaction = jest.fn();
    queryRunner.rollbackTransaction = jest.fn();
    queryRunner.release = jest.fn();

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        {
          provide: Connection,
          useClass: ConnectionMock,
        },
      ],
    }).compile();
  });

queryRunner의 메서드들도 모두 모킹을 시켜준다.

그런다음

    it('타이어 생성 성공 - 이미 해당 trim 이 db에 있음', async () => {
      //given
      const saveTireDtos: SaveTireDto[] = [
        {
          id: 'candycandy',
          trimId: 5000,
        },
        {
          id: 'ronaldo',
          trimId: 6000,
        },
      ];
      mockUserRepository.findOne
        .mockResolvedValueOnce({
          id: 1,
          username: 'candycandy',
          password: 'password',
        })
        .mockResolvedValueOnce({
          id: 2,
          username: 'ronaldo',
          password: 'password',
        });

      mockTrimRepository.findOne
        .mockResolvedValueOnce({
          id: 1,
          trim_id: 5000,
        })
        .mockResolvedValueOnce({
          id: 2,
          trim_id: 6000,
        });

      //when
      const resultMsg = await service.createTire(saveTireDtos);

      //then
      expect(resultMsg).toEqual(TIRE_SAVE_SUCCESS_MSG);
    });
  • 비동기적으로 데이터를 받아오므로 mockResolvedValueOnce를 통해 데이터를 받아온다. 위 코드는 반복문으로 두개의 데이터를 받아오므로 저런식으로 chaining을 이용했다.

구글링을 통해 createQueryRunner를 모킹하는 법을 알게 되었고 적용했다.
처음에는 nestjs에서 jest를 이용한 단위테스트가 어려웠는데, 내가 테스트하고 싶은 로직, 메서드 이외에는 모두 가짜로 모킹시켜주는 것을 알게 되었더니, 조금 이해하였다. 나는 테스트를 굉장히 중요하게 생각한다. 테스트가 없으면 너무 불안하다..

728x90