- 소셜 블로깅 사이트(e.g. Medium.com) Backend API 구현 프로젝트
- 기본 CRUD, 태그, 팔로우, 좋아요, 토큰 인증 구현
- 테스트 코드를 기반으로 실무자 PR 리뷰를 통한 완성도 제고
- 2명으로 2달간 진행(2023.04.11~2023.06.13)
- 오픈 소스 RealWorld Backend API 사양 준수
- User API
- Article API
- Comment API
Testcontainers를 통한 멱등성 있는 테스트 환경 구축
- 이슈: 다른 사람이 프로젝트를 내려 받아 테스트 코드를 실행하면, DB 관련 테스트는 전부 실패
- 원인: 테스트 환경과 동일한 DB를 다른 로컬 환경에서 자동으로 구성하도록 구현하지 못함
- 해결 과정
- 코드 리뷰를 통해 힌트를 얻어 Testcontainers 라이브러리를 적용하여 테스트 시 자동으로 DB를 구축하는 데에 성공했지만, 테스트 클래스마다 Docker 컨테이너가 실행되고 종료되어 테스트가 지연
- 모든 테스트가 종료될 때까지 컨테이너를 재사용할 수 있는 방법이 필요하여 Testcontainers 공식 문서와 여러 기술 블로그 확인 결과, application.yml 파일에서 데이터 소스를 Testcontainers로 지정하면 재사용할 수 있음을 확인
- 개발 환경과 테스트 환경의 DB 설정을 분리하여 application.yml 파일을 작성하고 @ActiveProfiles를 통해 테스트 코드에 적용
- 결과: 컨테이너를 재사용하도록 최적화하여 테스트 소요 시간을 1/3로 축소, 다른 환경에서 모든 테스트가 통과됨을 확인, 이를 통해 다른 사람이 테스트를 실행하면서 애플리케이션이 어떻게 동작하는지 파악 가능
@JsonTypeInfo 사용으로 DTO 구조 단순화
- 이슈: API 요청/응답에 사용되는 DTO 내부의 이너 클래스 사용으로 코드 복잡도 증가
- 원인: API Spec에 맞게 JSON 데이터 이름을 정하려면 DTO 내부에 이너 클래스 생성 필요
- 해결 과정
- @JsonRootName을 통해 이너 클래스 없이 JSON 데이터 이름을 지정할 수 있도록 개선
- 하지만 @JsonRootName이 필요 없는 경우에도 적용되어 API Spec 위반 사례 발생
- 일부 DTO만 적용될 수 있도록 @JsonTypeInfo, @JsonTypeName을 함께 사용하여 @JsonRootName 대체
- 결과: DTO 구조 단순화 및 코드 가독성 개선
Slug
- Slug 정의
- URL에서 웹페이지 내용을 함축하는 단어로 이루어진 구절, 검색 엔진 최적화에 사용됨
- 프로젝트 내 Article, Comment 관련 API Endpoint에 포함(총 8곳)
POST /api/articles/:slug/favorite
GET /api/articles/:slug/comments
- 요구사항
- Slug가 Article(게시글) 내용을 함축하고 유일성을 갖게 해야 함
- 구현과정
- 게시글 내용을 함축하는 제목을 기반으로 Slug를 생성하도록 구현
- 유일성 문제는 게시글이 생성될 때 만들어지는 PK 번호가 접미사로 붙도록 하여 해결
- 그런데 insert 되기 전에 PK 번호를 알아내어 insert 하는 MySQL 쿼리가 존재하지 않음
- 이에 Article 테이블의 마지막 PK를 조회 후 Slug 접미사 생성 파라미터에 +1을 추가하도록 구현(해당 코드)
- Article Update의 경우 Slug 접미사에 Article PK가 그대로 사용되어야 하므로 Slug 생성 시 기존 PK 사용하도록 구현(해당 코드)
- 향후 Slugify 라이브러리를 적용하여 완성도 향상
List Articles
- 요구사항
- 게시글 작성자, Tag, 게시글을 좋아한 사람으로 검색할 수 있어야 함
- 조회 결과를 페이징할 수 있어야 함(기본값: 1페이지당 게시글 20개)
- 로그인 여부에 따라 게시글 좋아요, 작성자 팔로우 여부가 다르게 표시되어야 함
- 구현과정
- Java 11
- Spring Boot 2.7.10
- JUnit 5
- Spring Security 5.7.7
- Java JWT 4.4.0
- MySQL Server 8.0.29
- MyBatis 3.5.11
- Gradle 7.6.1
사전 설치
- Java 11
- Docker Desktop: Testcontainers에서 DB 구동을 위해 필요
- Gradle로 프로젝트 빌드 시 Testcontainers를 통해 테스트 코드 실행됨
- MySQL Server 8.0.29
프로젝트 구동
- 프로젝트 파일을 다운받아 압축 해제 후 터미널로
gradlew build
실행 - 빌드가 완료되면 다음 경로에서 테스트 결과 확인 가능
프로젝트 루트 폴더/build/reports/tests/test/index.html
- MySQL DB 초기화
- Workbench를 통해 루트 계정으로
realworld
schema 생성 필요 application-dev.yml
에서 username, password 알맞게 수정spring: datasource: driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy url: jdbc:log4jdbc:mysql://localhost:3306/realworld username: ******** password: ********
- 앱 구동 전
src/main/resources/db/V1_create.sql
파일로 테이블 생성 필요
- Workbench를 통해 루트 계정으로
프로젝트 루트 폴더/build/libs
로 이동 후 터미널로java -jar realworld-0.0.1-SNAPSHOT.jar
실행http://localhost:8080/
을 호스트 주소로 하여 API Endpoints 사용 가능- 실행 후 터미널에서
Ctrl + C
입력하여 종료 가능
- 프로젝트 회고: https://bit.ly/3Jodl1I
- 프로젝트 대시보드: https://bit.ly/3JkAzpr
- 작업현황, 개발수칙, 트러블 슈팅, 참고 자료, 협업 내용 정리