[우아한테크코스 4기] 220705 F12 개발일지

Jihoon Oh·2022년 7월 5일
0

우아한테크코스 4기

목록 보기
16/43
post-thumbnail

오늘 진행한 일

GitHub Actions를 통한 빌드 Workflow 설정

이전에 사이드 프로젝트를 하면서 배운 것 중 하나가 바로 Pull Request를 날릴 때 GitHub Actions 기능으로 빌드를 한 번 돌려보는 workflow를 작성하는 것이었다. 원래는 Pull Request 전에 빌드를 해 보고 성공하면 Pull Request를 날리는 것이 정석이다. 하지만 그 과정을 깜빡하고 빠뜨릴 수도 있는데, Pull Request 시 자동으로 GitHub에서 빌드를 하도록 설정해 혹여나 문제가 있는 코드를 그대로 merge 시키는 일을 방지할 수 있었다.

다만 사이드 프로젝트 당시에는 다른 팀원이 작성한 workflow를 보고 그냥 아 이렇구나 하고 넘어간 정도였기 때문에, 막상 우리 프로젝트에 적용하려니 막막했다. 구현해야 할 workflow는 다음과 같았다.

  1. backend 폴더에 수정이 발생한 Pull Request가 열린다. (backend 이외의 디렉토리는 감지 x)
  2. 코드를 내려받고 브랜치를 변경한다.
  3. backend 폴더로 이동한다.
  4. JDK를 세팅한다.
  5. gradlew 권한을 상승시킨다.
  6. 빌드를 실행한다.

사이드 프로젝트 당시 팀원이 작성했던 스크립트는 1, 3번이 없이 2 → 4 → 5 → 6의 과정을 거쳤었는데, MAT.ZIP은 프론트엔드와 백엔드의 레포지토리가 분리되어 있었기 때문에 백엔드 레포지토리에서는 루트 경로에서 모든 작업을 해도 됐었다. 하지만 F12 레포지토리는 루트 경로에 frontend, backend 디렉토리로 나뉘어져 사실상 프로젝트 2개가 합쳐져 있는 형태이기 때문에 그냥 루트 경로에서 모든 작업을 실행하면 안된다. 왜냐면

  • 백엔드 workflow는 backend 디렉토리 내부의 스프링 프로젝트에 대해서 작동해야 하는데 루트 경로를 잡아버리면 프론트엔드 팀원들이 frontend 디렉토리에 Pull Request를 날려도 workflow가 작동한다.
  • gradle 빌드는 backend 디렉토리에서 해야 한다. 만약 루트 디렉토리에서 gradle 빌드를 하려 하면 gradle이 없어 동작하지 않을 것이다.

의 이유 때문이었다. backend 디렉토리의 수정 사항에 대해서만 빌드해야 한다는 부분은 나중에 dev_front, dev_back의 형태로 개발 브랜치를 프론트엔드와 백엔드로 구분해서 유지한다면 해당 브랜치에 PR이 열릴 경우에만 workflow를 실행시키도록 하면 되겠지만, 현재는 프로젝트 규모가 작아 dev 브랜치를 유지하지 않고 main 브랜치에 PR을 보내고 있기 때문에 workflow의 필터를 브랜치로 잡지 않고 backend 디렉토리로 잡았다.

결과적으로 다음과 같은 yaml 스크립트가 완성됐다.

name: backend                                         #...(1)

on:
  push:
    paths:
      - 'backend/**'
  pull_request:
    paths:
      - 'backend/**'                                  #...(2)

defaults:
  run:
    working-directory: backend                        #...(3)

jobs:
  build:
    runs-on: ubuntu-latest                            #...(4)
    steps:
      - name: checkout
        uses: actions/checkout@v3                     #...(5)

      - name: set up JDK
        uses: actions/setup-java@v3                   #...(6)
        with:
          java-version: '11'
          distribution: 'temurin'
  
      - name: grant execute permission for gradlew    #...(7)
        run: chmod +x gradlew
      
      - name: gradle build                            #...(8)
        run: ./gradlew build
  • (1): workflow의 이름을 지정한다.
  • (2): workflow가 실행될 조건을 지정한다.
    • on의 하위에 push, pull_request가 있으므로 push와 PR 동작에 대해 workflow가 실행된다.
    • paths로 특정 디렉토리에 대한 workflow를 지정할 수 있다. 위 스크립트 대로면 backend 디렉토리의 하위 디렉토리에 push, PR이 일어나면 workflow가 실행된다.
    • 브랜치를 기준으로 진행하려면 branches: [ ... ]의 형태로 특정 브랜치를 지정해 줄 수 있다.
  • (3): 작업을 진행할 기본 디렉토리를 지정한다. 모든 job은 backend 디렉토리로 이동한 이후 실행된다.
    • jobs 아래의 job들 간에 디렉토리 위치 등을 공유하지 않기 때문에, 한 작업을 실행하고 나면 다시 루트 디렉토리로 이동한다. 따라서 default working-directory를 지정해 매 작업 실행 전 이동할 디렉토리를 지정해 준 것이다.
  • (4): 작업이 진행될 환경을 지정한다.
  • (5): checkout 작업을 진행한다. checkout은 GitHub에서 제공하는 기능으로, 코드를 내려받고 브랜치를 변경하는 작업을 진행한다. 참고
  • (6): JDK를 세팅한다. setup-java 역시 GitHub에서 제공하는 기능이다. setup-java와 유사하게 setup-python, setup-node 등 다양한 환경에 대한 세팅 기능이 제공된다. 참고
    • 스크립트에서는 Open JDK 중 하나인 temurin의 11 버전을 세팅했다.
  • (7): gradle 빌드가 정상적으로 진행될 수 있도록 권한을 부여해준다.
  • (8): gradle 빌드를 실행한다.

이렇게 작성한 파일을 루트 디렉토리의 .github 디렉토리에 넣어주면, 이후에 진행되는 push와 PR에 대해 workflow가 실행된다. 실제로 오늘 진행한 PR들에 대해서 빌드가 진행되는 것을 확인할 수 있었다.


다만 브랜치를 잡지 않고 push 설정을 해주는 바람에 fork된 레포지토리에서 작업하는 과정에서도 workflow가 동작해버렸다. 다음 스프린트 부터는 프론트, 백 각각 dev 브랜치를 만들기로 했으니 해당 브랜치로 Pull Request가 발생할 때만 workflow가 실행되도록 수정할 필요가 있을 것 같다.

CORS 설정

내일부터 로컬에서 프론트엔드와 백엔드 간의 연결 작업 및 테스트를 진행하기로 했다. 때문에 그 전에 정상적으로 api 연결이 될 수 있도록 CORS 설정을 해 줄 필요가 있었다. 아직 인증 / 인가 과정이 들어가지는 않은 만큼 preflight 요청에 대한 처리를 해 주지는 않았고, origin 역시 정해지지 않았으므로 모든 origin에 대해 CORS를 허용해주었다.

@ControllerAdvice 작성

비즈니스 로직에서 커스텀 예외를 만들고 적절한 상황에 커스텀 예외를 던지도록하는 코드는 다 작성해주었지만 발생하는 예외에 대한 처리는 아직 하지 않고 있었다. 물론 프론트엔드쪽에서 예외가 발생할 상황을 먼저 차단해주기는 하겠지만, 백엔드 쪽에서도 예외 처리를 통해 이중 검증을 해 줄 필요가 있었다. 만약 예외처리를 해주지 않고 프론트엔드의 처리에 의존한다면 Postman 같이 클라이언트 서버를 거치지 않고 요청을 하는 경우에 대처할 수 없기 때문이다. 때문에 ControllerAdvice를 만들어서 예외 처리를 진행해 주었다. 이 때, invalid value 관련된 exception과 not found 관련된 exception으로 계층을 나누어 처리해주었다.

오늘 발생한 이슈

intellij 버그: MockMvc가 @Autowired되지 않는다는 가짜 오류가 나옴

CORS 설정을 하고 정상적으로 CORS 설정이 되었는지 테스트 코드를 작성해야 했다. 이 때 테스트 코드에는 MockMvc를 사용했는데, MockMvc가 @Autowired되지 않는다는 경고가 떴다.

그래서 설정을 점검해봤는데, @SpringBootTest@AutoConfigureMvc가 붙어있기 때문에 @Autowired가 정상적으로 되어야 하는데도 불구하고 되지 않는다는 경고가 발생하고 있었다. 그렇다고 진짜 @Autowired가 안되는 것도 아닌 것이, 수없이 테스트 코드를 실행해보고 빌드를 돌려봐도 정상적인 결과가 나왔다. 그래서 결론적으로는 인텔리제이가 제대로 컴포넌트 스캔을 하지 못하는 버그라고 정리하게 되었다.

문제는 저렇게 경고가 뜨면 MockMvc를 쓰는 코드의 커밋마다 경고가 뜬다는 점. 물론 정상적으로 테스트 코드도 돌아가고 빌드도 되니까 크리티컬한 문제는 없지만, 팀원들 모두 저 경고에 대해 기분나빠했다. 그래서 테스트 코드에서 MockMvc를 직접 생성하는 코드도 작성해봤다.

@SpringBootTest
class WebConfigTest {

    private final MockMvc mockMvc;
    
    @Autowired
    public WebConfigTest(WebApplicationContext webApplicationContext) {
        this.mockMvc = webAppContextSetup(webApplicationContext).build();
    }
    ...
}

하지만 이렇게 하니 다른 MockMvc를 쓰는 테스트 코드인 REST Docs 관련 코드에서 RESTDocs 설정을 해주기가 불편하다는 단점(@AutoConfigureRestDocs)가 제대로 작동하지 않는다는 문제가 있었다.

결국 경고가 뜨는 버그로 인해 보기 불편하기는 하지만, 정상적으로 작동을 하기 때문에 그냥 경고를 무시하고 사용하기로 결정했다.

내일 목표

현재 컨트롤러 레이어에 대한 단위 테스트가 진행되지 않고 있다. 원래는 REST Docs를 만드는 과정에서 컨트롤러 테스트를 함께 진행하려고 했는데, REST Docs를 만들 때 예외 사항에 대한 문서를 만들 필요는 없으므로 컨트롤러 테스트로 사용하기에는 부적절하다고 생각했다. 그래서 내일 mocking을 활용한 컨트롤러 슬라이스 테스트를 만들어 정상 응답과 예외 처리(ControllerAdvice의)에 대한 검증을 진행하는 테스트 코드를 작성하기로 했다.

그리고 데모 데이가 이틀 앞으로 다가온 만큼, 데모 데이때 데모를 보여줄 수 있도록 프론트엔드 코드와 백엔드 코드를 연결해서 로컬 환경에서 정상적으로 작동하는지 테스트 하고 버그가 있으면 수정하는 시간을 가지기로 했다.

profile
Backend Developeer

0개의 댓글