프로젝트
컬렉션 페치 조인의 페이징
문제 상황 엔티티 연관관계 Feed는 FeedImage와 일대다 연관관계를 가진다. Feed는 member와 다대일 연관관계를 가진다. FeedImage와 Member는 모두 지연 로딩 설정되어있다. @Entity @SuperBuilder @Getter @NoArgsConstructor public class Feed extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @Lob private String content; private int hits; private int commentCount; @Convert(converter = FeedStatu..
로그인 및 권한 체크 리팩토링(with AOP)
현재 Album 프로젝트의 로그인 및 관리자 체크는 아래와 같은 과정으로 이뤄지고 있다. Controller 계층의 메소드에서 AlbumUtil.checkLogin(PrincipalDetails)를 호출한다. checkLogin()은 로그인 상태이면 로그인 유저 정보를 담고 있는 MemberDto를 반환하고, 로그인 상태가 아니면 null을 반환한다. Service 계층의 메소드를 호출할 때 checkLogin()의 반환 값을 파라미터로 넘겨준다. Service 계층 메소드 내부에서 로그인이 되어 있지 않거나, 관리자 권한이 필요한데 관리자 권한을 갖지 않고 있는 경우에 예외를 발생시킨다. 아래는 Album 프로젝트의 로그인 및 권한 체크 과정을 이해하기 위한 예시 코드들인 AlbumUtil.checkL..
Redis Cache를 활용한 피드 조회 성능 개선
Album 프로젝트의 로그 기능을 구현한 후, 해당 기능을 통해 Album 프로젝트의 주요 기능들의 동작 과정을 확인해보았다. 주요 기능들 중 피드 상세 조회 기능이 매우 높은 비용으로 수행되고 있음을 확인할 수 있었다. 피드 상세 조회 기능의 동작 과정 ViewController.feedPage()가 호출되고, 피드 상세 페이지(HTML)이 반환된다. Ajax를 통해 피드 데이터를 반환하는 FeedController.getFeed(), 피드 댓글 데이터를 반환하는 CommentContoller.getFeedComments()가 호출된다. 피드 데이터, 피드 댓글 데이터를 반영하여 렌더링된 피드 상세 페이지가 클라이언트에게 보여진다. 피드 상세 조회 기능의 높은 수행 비용 피드 데이터와 피드 댓글 데이터..
스프링 AOP를 이용한 로그 기능
Album 프로젝트는 초기에 로그 기능의 필요성을 고려하지 못하여 로그 기능 없이 배포되었다. 이후 어플리케이션의 동작 과정, 에러 발생 지점 파악, 성능 분석 등을 위해 로그 기능은 거의 필수적이라는 것을 알게 되었다. 그래서 스프링 AOP를 통해 아래의 요구사항을 충족하는 로그 기능을 Album 프로젝트에 추가해보고자 한다. Persistence, Business, Presentation 계층의 public 메소드 호출 및 종료에 대한 로그를 남긴다. 클라이언트 요청 별로 로그를 구별할 수 있다. 로그에는 메소드 정보, 메소드 호출 깊이, 수행 시간이 표현된다. 메소드 정상 종료와 예외 종료를 구별할 수 있다. 로그 클래스 LogId LogId는 이번 로그 기능의 가장 기본이 되는 클래스이다. Log..
Scale Out 환경에서 세션 정합성 문제 해결
Album 프로젝트는 트래픽 부하 분산을 위해 스케일 아웃(Scale Out) 방법을 사용하고 있다. 스케일 아웃 방법을 사용할 경우, 서비스는 다중 서버 환경으로 동작하게 된다. 다중 서버 환경에서는 각각의 서버가 각각의 세션 저장소를 독립적으로 갖게 되어, 서버 간에 세션 정보를 공유하지 못하는 문제가 발생한다. Album 프로젝트도 이와 관련하여 문제가 발생했다. Album 프로젝트는 세션을 통해 로그인 정보를 관리하고 있다. 그런데 다중 서버 환경이 구축되다 보니 A서버에서 로그인을 했더라도, B 서버로 다시 접속하게 되면 로그인 상태가 유지되지 않는 문제가 발생했다. 다중 서버 환경에서의 세션 공유 문제를 해결하기 위한 대표적인 방법으로는 Sticky Session, Session Cluster..
API 명세서 작성
Album 프로젝트 전의 프로젝트들을 진행할 때는 API 명세서를 구글 독스, 노션 등에 직접 작성하였다. 사람이 API 명세서를 직접 작성하다보니, 오타나 특정 내용을 누락하는 경우가 꽤 있었다. 그리고 명세서 유지 보수 측면에서도 꽤 비효율적이었다. 그래서 Album 프로젝트에서는 어플리케이션을 통해 API를 자동으로 생성하는 방법을 사용하고자 하였다. 스프링 어플리케이션의 대표적인 API 작성 방법으로는 Spring Rest Docs와 Swagger가 있었고, 장단점 비교를 통해 적절한 방법을 선택하고자 하였다. 비교 Spring Rest Docs Spring Rest Docs의 가장 큰 장점은 서비스 코드에 불필요한 영향을 주지 않는다는 것이다. 또한 API 명세서가 작성되기 전에 테스트가 성공적..

배포 시 환경변수 관리
GithubActions를 통해 Album 프로젝트를 ELB에 배포하면서, 한 가지 고민 거리가 생겼다. Album 프로젝트는 env.properties라는 파일에서 환경 변수를 관리하고 있었다. 환경 변수에는 DB 정보, Oauth2 정보 등 민감 정보가 포함되어 있어서, github에는 업로드를 하지 않고 있었다. 배포를 하기 전에는 로컬에서만 어플리케이션을 실행하고, 테스트 코드를 실행했기 때문에 github에 env.properties를 업로드하지 않아도 아무 문제가 없었다. 그러나 GithubActions를 통해 프로젝트 배포를 시도하면서, github에 env.properties를 업로드 하지 않는 것이 문제를 발생시켰다. 우선 GithubActions로 프로젝트를 배포할 때는 테스트가 먼저 ..
AttributeConverter 활용
Album 프로젝트를 진행하면서, 한 가지 문제점이 생겼다. enum 값의 특정 순서대로 데이터를 정렬하여 조회하고 싶은데, JPA에서는 이러한 기능을 직접적으로 제공하지 않는 것이 었다. 예를 들어 FIRST, NORMAL, LOCKED, WITHDRAW라는 enum 값들이 있다. 데이터베이스 컬럼에는 해당 값들이 그대로 저장되어있는 상황이다. JPA에서 직접 제공하는 기능으로는 해당 컬럼을 기준으로 오름차순 또는 내림차순으로 정렬만 할 수 있다. NORMAL > LOCKED > WITHDRAW > FIRST 라는 특정한 순서대로 정렬할 수 없는 것이다. 이러한 문제를 해결할 수 있는 방법을 찾아보다가 AttributeConverter라는 기능을 알게되었다. AttributeConverter Attri..
테스트 환경 통합하기
Album 프로젝트의 테스트들은 모두 스프링 로드 하에 실행된다. Presentation 계층의 테스트나 Rest Docs 테스트는 @WebMvcTest를 통해 등록되는 빈을 줄이고 Mock을 사용하긴 하지만, 스프링을 구동하기는 마찬가지이다. 이러한 스프링 구동 하에 실행되는 테스트의 장점은 실제 서비스 환경과 매우 유사한 환경에서 테스트를 실행해볼 수 있다는 것이다. 하지만 단점도 있는데, 스프링을 로드하는 과정 때문에 테스트 속도가 비교적 느리다는 것이다. 이러한 단점을 보완할 수 있는 방법이 있는 지 여러 블로그들의 글을 뒤적이다가 알게된 사실이 있다. 어찌보면 당연하지만 스프링 통합 테스트의 속도 문제는 스프링 로드 횟수에 가장 큰 영향을 받으며, 각 테스트들이 공통된 환경에서 실행되도록 구현한..