JAVA/우아한테크코스 4기

[220722] 우아한테크코스 2차 데모데이 후기

kth990303 2022. 7. 24. 15:23
반응형

22일 금요일에 우테코 2차 데모데이가 있었다! 우아한테크코스에선 레벨3 두 달동안 프로젝트를 진행한다. 그리고 그 기간 중 2주마다 한 번씩 데모데이를 실시한다. 애자일하게 개발하도록 하여 2주마다 결과물을 업그레이드시켜서 발표하는 것이다. 

우리는 모임 기반 롤링페이퍼 서비스 플랫폼을 준비했으며, 이번 2차 데모데이에는 모임, 회원에 대한 인증 및 인가 기능, 그에 따른 롤링페이퍼와 메시지 기능 추가작업을 진행했다. 그리고 백엔드에서는 Jenkins + AWS + Docker를 이용한 배포 작업이 진행됐으며, 프론트에서는 NGinx + AWS + Jenkins 배포 작업을 완료했다~

 

이번 2차 데모데이 준비과정과 후기, 그리고 그 이후를 간단하게 포스팅하도록 하겠다.

 

참고로 우리가 개발하는 서비스 깃허브는 여기서 볼 수 있다.

https://github.com/woowacourse-teams/2022-nae-pyeon

 

GitHub - woowacourse-teams/2022-nae-pyeon: 내 마음을 편지로, 내 편 💌

내 마음을 편지로, 내 편 💌. Contribute to woowacourse-teams/2022-nae-pyeon development by creating an account on GitHub.

github.com


Github 프로젝트 기능 도입

깃허브에 존재하는 프로젝트라는 기능을 이용하면 진행해야될 이슈, 현재 진행중인 이슈, 진행완료된 이슈를 한 눈에 볼 수 있다. 이슈뿐만 아니라 PR 또한 깃허브 프로젝트 설정을 해두면 위 창에서 확인할 수 있다. 해당 이슈에 PR을 날리고 resolve #{이슈번호} 혹은 close #{이슈번호}를 작성해주면 해당 이슈가 TO do에서 In progress 또는 Done으로 이동되는 것을 확인할 수 있다.

 

우리는 이슈에만 프로젝트 설정을 해두고, PR에는 별도로 설정하지 않기로 했다. Issue 기반 PR 작성이 원칙이기 때문에 이슈와 pr 둘 다 프로젝트에 보이도록 세팅할 경우, 동일한 이슈가 두 개 이상 보여지므로 오히려 가독성을 해친다고 판단했기 때문이다.

 

처음에는 깃허브 프로젝트를 도입하지 않을까 생각하다가, 진입장벽이 높지 않고 한눈에 우리 이슈 진행상황을 확인할 수 있다는 장점 덕분에 프로젝트를 도입했다. 이 도입은 꽤나 만족스러웠다.


간단한 인증/인가 기능 추가

@AuthenticationPrincipal 어노테이션으로 인증/인가를 처리한다.

이번 스프린트 2에서는 로그인 기능과 그에 따른 인증 및 간단한 인가 처리를 하는 것이 목표였기 때문에 ArgumentResolver를 통한 payload값 확인작업과 LoginInterceptor를 통한 토큰 발급처리를 진행해주었다. 

특정 모임 uri에 들어가 접속하려고 하면, 컨트롤러에 요청이 들어가기 전에 먼저 인터셉터에서 해당 로그인 멤버의 json web token (jwt)가 올바른 토큰인지를 파악해준다. 올바르지 않은 토큰이라면 JwtTokenProvider에서 해당 토큰에 관한 적절한 예외처리를 터뜨려준다. 이 작업에 대해 더 자세히 알고 싶다면 아래 포스팅을 참고하자.

https://kth990303.tistory.com/350

 

[Spring] 인증 토큰을 생성하는 JwtTokenProvider를 알아보고 테스트를 작성해보자

그동안 미루고 미루어왔던 JwtTokenProvider에 대해 알아보고, 테스트 코드를 작성하는 법을 작성하려 한다. JwtTokenProvider는 로그인 인증 과정을 처리할 때 반드시 필요한 access Token을 생성해주는 역

kth990303.tistory.com

올바른 토큰인지 검증된다면 해당 URI의 Mapping에 맞는 컨트롤러 메서드를 실행해준다. 이 작업은 DispatchServlet <-> Handler Adaptor, Handler Mapping 쪽에서 이루어진다. 핸들러 어댑터에서 해당 매핑을 실행할 때, 위에서 언급한 ArgumentResolver에서 해당 토큰으로 payload값을 반환해주고 그 값을 통해 유저 인증/인가 정보를 확인해 해당 모임의 가입자가 맞는지 확인할 수 있다.

 

이렇게 해서 해당 모임의 가입자라면 모임의 롤링페이퍼와 메시지를 확인할 수 있게 해주고, 그렇지 않다면 열람할 수 없도록 막아놓게 구현해놓았다. 참쉽죠?


꼼꼼한 인수테스트 작성

테스트코드를 작성하는 데에도 절대 소홀히 하지 않았다. 인수테스트는 특히, 사용자의 시나리오와 비즈니스 기능을 직접적으로 테스트하는 부분이므로 더더욱 예외 케이스를 놓치지 않도록 꼼꼼히 작성하였다.

 

AcceptanceFixture

public static TokenResponseDto 회원가입_후_로그인(final MemberRegisterRequest member) {
    회원_추가(member);
    return 로그인_응답(new TokenRequest(member.getEmail(), member.getPassword()))
            .as(TokenResponseDto.class);
}

public static Long 모임_생성(final TokenResponseDto tokenResponseDto) {
    final TeamRequest teamRequest = new TeamRequest(
            "woowacourse-4th",
            "테스트 모임입니다.",
            "testEmoji",
            "#123456",
            "나는야모임장"
    );
    return 모임_추가(tokenResponseDto, teamRequest).as(CreateResponse.class)
            .getId();
}

public static ExtractableResponse<Response> 회원_추가(final MemberRegisterRequest member) {
    return member_post(member, "/api/v1/members");
}

위와 같이 테스트를 위한 픽스쳐를 미리 구현해놓아, 다른 인수테스트 클래스에서 위 픽스쳐를 사용해 전처리를 편하게 해놓을 수 있도록 해놓았다.

 

RollingpaperAcceptanceTest

@Test
@DisplayName("특정 회원에게 롤링페이퍼를 여러 번 생성할 수 있다.")
void createRollingpapersToMember() {
    final TokenResponseDto tokenResponseDto1 = 회원가입_후_로그인(member1);
    final Long teamId = 모임_추가(tokenResponseDto1, teamRequest).as(CreateResponse.class)
            .getId();

    final TokenResponseDto tokenResponseDto2 = 회원가입_후_로그인(member2);
    모임_가입(tokenResponseDto2, teamId, new JoinTeamMemberRequest("영환이형도좋아요"));

    // when: seungpang이 yxxnghwan에게 롤링페이퍼 2개 작성
    final RollingpaperCreateRequest rollingpaperCreateRequest1 = new RollingpaperCreateRequest("하이알렉스", 2L);
    final Long rollingpaperId1 = 회원_롤링페이퍼_생성(tokenResponseDto1, teamId, rollingpaperCreateRequest1)
            .as(CreateResponse.class)
            .getId();
    final RollingpaperCreateRequest rollingpaperCreateRequest2 = new RollingpaperCreateRequest("반가워", 2L);
    final Long rollingpaperId2 = 회원_롤링페이퍼_생성(tokenResponseDto1, teamId, rollingpaperCreateRequest2)
            .as(CreateResponse.class)
            .getId();

    // then: yxxnghwan이 받은 롤링페이퍼 조회
    final List<Long> actual = 나의_롤링페이퍼_조회(tokenResponseDto2, teamId).as(RollingpapersResponseDto.class)
            .getRollingpapers()
            .stream()
            .map(RollingpaperPreviewResponseDto::getId)
            .collect(Collectors.toUnmodifiableList());

    assertThat(actual).contains(rollingpaperId1, rollingpaperId2);
}

@Test
@DisplayName("존재하지 않는 회원에게 롤링페이퍼를 생성하는 경우 예외를 발생시킨다.")
void createRollingpaperWithNotFoundMember() {
    final TokenResponseDto tokenResponseDto1 = 회원가입_후_로그인(member1);
    final Long teamId = 모임_추가(tokenResponseDto1, teamRequest).as(CreateResponse.class)
            .getId();

    final RollingpaperCreateRequest rollingpaperCreateRequest = new RollingpaperCreateRequest("하이알렉스", 2L);
    final ExtractableResponse<Response> response = 회원_롤링페이퍼_생성(tokenResponseDto1, teamId,
            rollingpaperCreateRequest);

    assertThat(response.statusCode()).isEqualTo(HttpStatus.NOT_FOUND.value());
}

위와 같이 단순히 기능만 테스트하는 것이 아닌, 이 기능을 어떻게 사용하는지 세부적으로 분류하여 꼼꼼히 테스트하도록 진행했다.

꼼꼼한 테스트 덕분에 현재 테스트개수가 2차 데모데이 시기임에도 불구하고 146개나 완성됐다.


AWS + MySQL + Jenkins 배포 작업

스프린트2에서는 배포와 함께 CI/CD 작업을 하고, 우리만의 도메인 주소를 구입하여 적용하는 과정을 진행해보았다. 우리는 CI/CD 툴로 Github Action과 Jenkins 중에 후자를 선택했다. 깃허브 액션은 CD 권한을 우아한테크코스에서 제공하지 않는다고 하기도 했고, 우리 팀원들이 Jenkins에 좀 더 익숙하기도 했기 때문이다. 프로젝트를 빠르게 배포하는 데에 집중했기 때문에 학습장벽이 낮은 Jenkins를 선택한 것은 큰 도움이 되었다.

우리는 Git flow 전략을 이용했기 때문에 feature 브랜치에서 작업을 하고 develop 브랜치로 PR을 날린 후에, 코드리뷰 과정을 거쳐 머지 작업이 일어난다. Jenkins를 이용하면 우리가 develop branch로 PR을 날릴 때, Jenkins에서 build 및 테스트 작업을 진행해준다. 빌드와 테스트가 통과되면 Jenkins에서 백엔드의 jar파일과 프론트의 React 빌드 파일들을 aws ec2 인스턴스로 보내준다. 그리고 젠킨스에서 알아서 ssh 명령어로 해당 파일을 실행해주어 배포환경에서 즉각적으로 실행할 수 있게 된다.


BE, FE 페어 프로그래밍

백엔드 크루들이 프론트엔드 작업을 해보기도 하고, 반대로 프론트 크루들이 백엔드 작업을 해보는 시간도 가져보았다.

회원가입과 모임 관련 api 및 UI/UX 작업, 그리고 mock test 코드를 작성해보았다. 서로 프론트, 백 상황에 대해 알 수 있어서 좋긴 했지만, 아무래도 해당 진영의 지식이 없다 보니 코드를 효율적으로 작성하기엔 무리가 있었던 방법인 듯하다.

 

다음에는 서로 코드를 직접 작성하기보단, 데일리 미팅으로 서로의 진행상황을 꼼꼼하게 공유하는 것으로 결론을 내렸다.


데모데이 당일

이번 데모데이 발표는 승팡, 소피아였다. 원래 나랑 소피아가 할지, 승팡과 소피아가 할지 결론이 나지 않았었는데, 지금 생각하면 정말 다행이었다. 내가 발표 전날에 매우 아팠었기 때문이다 ㅜㅜ

39도가 넘는 고열이 발생하기 시작했고, 소화불량 증세도 나타나기 시작했다. 역시 제일 중요한 건 건강관리라는 것을 다시 한 번 깨닫는 시기였다. 그렇게 발표전날은 질병결석을 하고, 발표 당일에는 열이 내려가서 다행히 등교할 수 있었다!

발표 대본이 아예 없는 갓팡과 차분히 발표를 매우 잘하는 킹피아

이 날 승팡은 아예 대본 없이 발표를 했는데, 그럼에도 불구하고 기똥차게 발표를 해주었다. 원래 승팡은 아예 대본없이 즉석으로 발표하는 스타일이라고 한다ㅋㅋ 소피아는 차분한 목소리로 간결하게 발표를 잘 이끄는 타입이었다. 나랑 제로, 알렉스, 도리는 아이패드 또는 노트북으로 응원문구를 작성해 높이 들어주었다!

 

이번 데모데이 회고는 역시 위 사진으로 대체한다~

데모데이 끝나고 맛있는 닭볶음탕도 먹고~ 신나게 보드게임카페에서 달무티랑 바퀴벌레포커를 하였다.

소피아는 보드게임 1도 못한다면서, 달무티에서 1등을 무려 두 번이나 했다ㅋㅋ 도리는 반대로 보드게임 좀 했었는데 달무티에서 꼴찌를 2번인가 3번인가 했다ㅋㅋㅋ

나는 1등, 2등, 3등, 꼴찌 다 해본듯.

 

오랜만에 술 없이 신나고 재밌게 놀았던 하루였다~

반응형