두벅스 서비스는 크게 ‘사용자 서버’ 와 ‘서비스 서버’ 로 나뉜다. 사용자 서버에서는 사용자 관련 기능과 인증 관련 토큰을 발급한다. 이를 서비스 서버에서 사용한다. 받아온 토큰이 두벅스 사용자 서버에서 발급된 토큰이 맞는지 검증하여 로그인 된 사용자로 인식한다.
두 서버의 역할이 분리된 덕분에 욜로가 서비스 서버에서는 Member 와 관련된 엔티티가 없다. 하지만 기능을 확장하다보면 사용자의 정보가 필요한 경우가 발생한다. 예를 들어 욜로가 러닝크루의 참여자 목록을 조회할 때 사용자의 아이디와 닉네임 정보가 필요하다. 사용자 서버에게 API 를 호출하여 사용자 아이디에 해당하는 닉네임 정보를 불러올 수도 있으나, 빈번하게 호출될 것이라고 예상되는 API 에게는 너무 낭비가 큰 방식이라고 생각하였다.
이 문제는 결국 애플리케이션 계층에서 ‘Member 엔티티’ 가 없었기 때문에 발생하는 문제였고, 애플리케이션 계층으로 오기 전에 데이터베이스 계층에서 정보를 가져오는 방식으로 해결하였다. 서버는 분리하였지만 바라보고 있는 DB 서버는 하나이기 때문에 DB 서버에는 Member 를 포함한 모든 테이블이 포함되어 있다. 따라서 JPA 를 이용하여 데이터를 조회할 때 Member 의 정보도 같이 불러오도록 구현하기 위해 DTO Projection
을 이용하였다.
DTO Projection 이란 JPA 를 통해 엔티티 객체로 받아왔던 값을 엔티티가 아닌 값으로 받아오는 방식이다. 엔티티 대체 값은 클래스도 가능하고 인터페이스도 가능하다. 하지만 클래스는 인터페이스와 달리 중첩으로 값을 선언할 수 없기 때문에 확장을 고려하여 인터페이스
로 생성하였다.
ParticipantDto.java
package com.dobugs.yologaapi.repository.dto.response;
public interface ParticipantDto {
Long getId();
String getNickname();
}
인터페이스로 DTO 생성 시 받아오는 컬럼명에 맞는 getter 를 선언하는 것만으로도 완성이다.
ParticipantRepository.java
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.dobugs.yologaapi.domain.runningcrew.Participant;
import com.dobugs.yologaapi.repository.dto.response.ParticipantDto;
public interface ParticipantRepository extends JpaRepository<Participant, Long> {
@Query(
value = "SELECT m.id AS id, m.nickname AS nickname\\n"
+ "FROM participant AS p\\n"
+ "LEFT JOIN member AS m\\n"
+ "ON p.member_id = m.id\\n"
+ "WHERE p.running_crew_id = ?1 AND p.status = ?2 AND p.archived = true\\n"
+ ";",
countQuery = "SELECT (p.id)\\n"
+ "FROM participant AS p\\n"
+ "LEFT JOIN member AS m\\n"
+ "ON p.member_id = m.id\\n"
+ "WHERE p.running_crew_id = ?1 AND p.status = ?2 AND p.archived = true\\n"
+ ";",
nativeQuery = true
)
List<ParticipantDto> findParticipants(final Long runningCrewId, final String participantType);
}
욜로가 프로젝트에는 Member 엔티티가 없으므로 NativeQuery
를 이용하여 직접 쿼리문을 작성하였다.
NativeQuery 를 사용하여 모든 값을 기본 클래스(ex. 직접 생성한 Java 객체 불가능) 로 받아와야 하는 단점은 있다. 또한 인터페이스 DTO 는 JPA 가 자체적으로 프록시 객체를 생성하여 받아오기 때문에, 이를 테스트하기 위해서는 별도의 구현체를 만들어야 한다.