티스토리 뷰

원인 확인

저번에 Github Actions을 사용해서 CI를 구축하는 블로그를 썼었다. 유용하게 사용하고 있을 때, intellij에서는 테스트가 통과되지만 빌드에서는 막히는 상황이 발생하였다.

 

intellij에서 테스트 돌렸을 때

 

inteliij 터미널로 테스트를 했을 때

 

물론 ./gradlew build도 잘 먹힌다. 

 

하지만 Github Actions이 돌리는 빌드에서는 오류가 뜬다.

 

위 사진을 봤을 때, 이상한 점이 있다. 바로 10개의 테스트가 돌아갔지만 실패한 테스트는 2개라는 것이다. 그래서 위 사진에서 실패한 테스트들의 공통점을 찾아보았다. 바로 테스트가 돌아갈 때 DB를 사용한다는 것이다. 

@DisplayName("이미 존재하는 이메일로 회원가입할 시 예외 발생")
    @Test
    void create_Exception_Duplicated_Email() {
        SignUpRequestDto signUpRequest = SignUpRequestDto.builder()
                .email("test@gmail.com")
                .password("test1234")
                .name("남세")
                .phoneNumber("010-1234-5678")
                .build();

        assertThatThrownBy(() -> userService.signUp(signUpRequest))
                .isInstanceOf(DuplicateEmailException.class);
    }

    @DisplayName("모든 조건 만족 시 회원가입 성공")
    @Test
    void signUp() {
        SignUpRequestDto signUpRequest = SignUpRequestDto.builder()
                .email("newTest@gmail.com")
                .password("test1234")
                .name("남세")
                .phoneNumber("010-1234-5678")
                .build();
        userService.signUp(signUpRequest);

        User findUser = userRepository.findByEmailValueAndPasswordValue(signUpRequest.getEmail(),
                signUpRequest.getPassword());
        assertThat(findUser.getRoleType()).isEqualTo(User.RoleType.USER);
    }

 

밑의 코드를 보면 레파지토리로 entity를 저장하는 로직을 볼 수 있다. 즉 db가 필요한 테스트다. 

public void signUp(SignUpRequestDto signUpRequest) {
        validate(signUpRequest);

        User user = User.builder()
                .email(Email.create(signUpRequest.getEmail()))
                .password(Password.create(signUpRequest.getPassword()))
                .name(Name.create(signUpRequest.getName()))
                .phoneNumber(PhoneNumber.create(signUpRequest.getPhoneNumber()))
                .build();
        userRepository.save(user);
    }

 

나는 현재 실제 DB와 테스트용 DB를 분리하여 사용하고 있다. 어쨌든 DB를 사용하고 있다는 말이니까 DB 관련 오류라고 생각했다. 역시나 details로 로그를 확인해보니까 아예 DB연결이 안됐었다. 

 

1. Github Actions yml 파일에 db 연결해주기

Github Actions 코드가 적혀진 yml 파일에 db를 연결해주면 될 것 같다.

- name: Mysql 설치
      uses: samin/mysql-action@v1
      with:
        host port: 3306
        container port: 3306
        mysql database: 'DB 이름'
        mysql user: '유저 이름'
        mysql password: 'mysql 비번'

 

하지만 위 코드를 추가해도 여전히 db 연결 자체가 안되고 있다.

 

2. 운영 DB와 테스트용 DB를 명시적으로 지정해주기

계속해서 해결방법을 찾고 있던 와중에 이런 댓글을 봤다. (출처 : OKKY)

 

나는 확실하게 분리가 되었다고 생각했지만, 사실 그렇지 않았던 것이다. 그래서 active profile로 직접 지정을 했다. 테스트용 yml에 가서 아래와 같이 설정해주었다. 

  profiles:
    active: test

 

그리고 테스트할 클래스 위에 아래 어노테이션을 붙여주면 명시적으로 지정해준 DB를 사용하게 된다.

@ActiveProfiles("test")

 

하지만 어림도 없었다. 하긴 DB 연결 자체가 안되는데 분리하는 것이 의미가 있나? 라고 생각했다. 다른 블로그들을 봐도 똑같은 플로우인데 왜 안되는지 궁금했다. 보니까 mysql을 지원하는 action을 들어가보니 test가 실패되어있는 상태였다.

 

원래라면 기존에 지원하는 action을 쓰는 것이 효율적이지만, 기존에 지원하는 action에 오류가 있어서 그냥 내가 만들어봐야 겠다고 생각했다. 

 

3. 직접 Mysql 설정하기

Github Actions는 기본으로 VM머신에서 돌아가고 있다. 정확하게 말하면 마이크로소프트가 가지고 있는 VM머신이 있고, Github Action을 통해 명령을 하면 VM에서 설정하는 방식이다. 도커는 자동으로 깔려있는 것 같다. 

 

일단 도커 컨테이너가 있는지 확인부터 해보자. 아래 명령어를 추가한다.

- name: Docker Process list
      run: docker ps -a

 

역시 도커는 기본으로 세팅 되어있다. 

 

컨테이너는 없는 상태니까 이미지도 없을거라고 생각하고 mysql image부터 받아보자. 

- name: Mysql 이미지 받아오기
      run: docker pull mysql
      
    - name: Docker 이미지 list
      run: docker images

 

한번 Image list를 보면 mysql image가 있는 것을 확인할 수 있다.

 

그럼 이제 mysql image를 구동시킬 컨테이너를 생성하고 실행시켜보자. 출력 역시 같이 해보도록 하겠다. 

    - name: Docker container 생성
      run: docker run --name 컨테이너이름 -e MYSQL_ROOT_PASSWORD=비번 -d -p 3306:3306 mysql
      
    - name: Docker Process list
      run: docker ps -a

 

그럼 아까와 달리 컨테이너가 작동중인 것을 볼 수 있다.

 

컨테이너에서 Mysql에 접속하고 DB연결을 하는 명령어는 다음과 같다.

- name: MySql Connection Text
      run: docker exec -i 컨테이너 이름 bash
      
    - name: Mysql 접속
      run: mysql -u root -p비번
      
    - name: DB 생성
      run: create database DB이름 default character set utf8;

 

하지만 실행해보면 아래와 같은 오류가 뜬다.

 

local Mysql 서버에 연결이 되지 않는다는 오류인데, mysql 실행이 되지 않아 발생하는 이유도 있다고 한다. 하지만 나는 컨테이너 위에서 mysql을 올리고 접근했기 때문에 실행은 되어있는 상태였다. 혹시 백그라운드로 실행해서 컨테이너가 충돌한게 아닐까? 라고 생각했다. 그래서 docker container 생성 명령어에서 -d 부분을 빼고 진행하였다. 

 

-d 옵션을 빼고 실행하니까 시간이 너무 오버된다. 되는지도 모르겠지만 시간이 너무 오래 걸려서 이런 방법은 안되겠다 싶었다. (25분이나 걸렸다)

 

어쩔 수 없이 다른 방법을 찾을 수 밖에 없었다. 

 

4. Mysql 대신 h2로 실행하기

처음에 테스트용 DB에 대해서 찾아볼 때, 거의 전부 다 h2를 사용하는 것을 봤다. 처음엔 이해가 가지 않았다. 왜냐하면 운영 서버는 mysql인데, 테스트를 h2로 하게 되면 DB 환경이든 설정의 차이점 때문에 테스트는 성공할 수 있어도 운영 서버에서 실패뜰 수 도 있지 않을까? 라고 생각하고 mysql을 정했었다. 하지만 Mysql은 도저히 방법이 생각나지 않으니까 h2로 실행한다. (귀찮아도 운영서버에서 한번 더 테스트해도 될 것 같다)

 

먼저 테스트용 yml 파일을 h2관련으로 바꾼다.

spring:
  datasource:
    url: jdbc:h2:mem:이름?serverTimezone=Asia/Seoul;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    driver-class-name: org.h2.Driver
    username: sa
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: true
        jdbc:
          time_zone: Asia/Seoul
    show-sql: true

  h2:
    console:
      enabled: true

 

url 부분에 이름이라고 지정했는데, 아래 명령어로 자신의 루트 경로에 이름.mv.db 파일을 생성해준다. 

touch 이름.mv.db

 

그럼 생성된 걸 볼 수 있다.

 

Github Action yml파일에서 Mysql 설정 코드들은 뺐다. 

name: backend

on:
  push:
    branches: [ "develop" ]
  pull_request:
    branches: [ "develop" ]

permissions: write-all

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: JDK 17 설치
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Gradle 명령 실행을 위한 권한을 부여
      run: chmod +x gradlew

    - name: Gradle 빌드
      run: ./gradlew build

    - name: 테스트 결과를 PR에 코멘트로 등록
      uses: EnricoMi/publish-unit-test-result-action@v1
      if: always()
      with:
       files: '**/build/test-results/test/TEST-*.xml'

 

드디어 성공! 조금 찝찝하긴 하지만 4일을 날린 것보다는 훨씬 낫다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday