- 발생한 문제 : 데이터 조회시 각 서비스의 독립된 데이터베이스 환경
예를 들어, 수업 서비스가 사용자 정보를 필요로 할 경우, 헤더에 포함된 JWT 토큰을 기반으로 사용자 서비스에서 데이터를 조회하는 방식으로 구현하였고 사용자가 수업 서비스의 정보를 필요로 할 경우도 동일한 방식으로 구현하였습니다.
이 과정에서 토큰 검증은 API Gateway에서 1차적으로 인증된 상태를 활용하였으며 각 서비스간에 발생하는 조회는 OpenFeign 통신 라이브러리를 활용하였습니다.
이를 과정을 통해 데이터 정합성을 유지하면서 서비스 간 결합도를 낮추고, 시스템의 확장성과 유연성을 높일 수 있었습니다.
Code
@FeignClient(
name = "scheduler-member-service",
path = "/feign-member",
url = "${scheduler_member_service_url:}",
configuration = MemberFeignErrorDecoder.class
)
public interface MemberServiceClient {
@Operation(
summary = "교사 정보 조회",
description = "토큰을 이용하여 정보 조회"
)
@GetMapping("teacher/info")
TeacherInfo findTeacherInfoByToken(
@RequestHeader(AUTHORIZATION) String token
);
@Operation(
summary = "학생 수업 조회",
description = "토큰을 이용하여 정보 조회"
)
@GetMapping("student/info")
StudentInfo findStudentInfoByToken(
@RequestHeader(AUTHORIZATION) String token
);
@Operation(
summary = "이용자의 아이디와 역할 조회"
)
@GetMapping("member/info")
MemberInfo findMemberInfoByToken(
@RequestHeader(AUTHORIZATION) String token
);
}
TestCode
wiremock을 이용하여 테스트를 진행하였으며 각 테스트간 서버의 독립성을 위하여 @BeforeEach와 @AfterEach를 주었고 Github에 push하면 Github-Actions에서 각 테스트가 실행되며 정합성을 검사하고 도커허브로 푸쉬됩니다.
@IntegrationTest
class CourseQueryServiceTest {
@Autowired
private CourseQueryService courseQueryService;
@MockitoBean
private MemberServiceClient memberServiceClient;
@Autowired
private WireMockServer wireMockServer;
@BeforeEach
void startWireMockServer() {
if (!wireMockServer.isRunning()) {
wireMockServer.start();
}
}
@AfterEach
void stopWireMockServer() {
if (wireMockServer != null) {
wireMockServer.stop();
}
}
@Test
@DisplayName("학생의 클래스 찾기")
void findStudentClasses() {
StudentInfo studentInfo = new StudentInfo("teacher_001", "Mr. Kim", "student_009", "Irene Seo");
when(memberServiceClient.findStudentInfoByToken(token))
.thenReturn(studentInfo);
StudentCourseResponse studentClasses = courseQueryService.findStudentClasses(token, mockYear, mockWeek);
assertThat(studentClasses)
.extracting(
"studentId", "studentName",
"mondayClassHour", "tuesdayClassHour", "wednesdayClassHour", "thursdayClassHour", "fridayClassHour",
"courseYear", "weekOfYear")
.containsExactly(
"student_009", "Irene Seo",
3, 2, 1, 4, 2,
mockYear, mockWeek
);
}
@Test
@DisplayName("교사에게 할당된 수업 수")
void findTeachersClasses() {
// Given
TeacherInfo teacherInfo = new TeacherInfo("teacher_001");
when(memberServiceClient.findTeacherInfoByToken(token))
.thenReturn(teacherInfo);
CourseList teachersClasses = courseQueryService
.findTeachersClasses(token, mockYear, mockWeek);
int size = teachersClasses.getClassList(FRIDAY).size();
assertThat(size).isEqualTo(1);
}
}