1. RestController
우리가 원하는 기능은 특정 id에 대한 병원 정보를 return하는 api입니다.
•
annotation
@RestController
@RequestMapping("/api/v1/hospitals")
public class HospitalRestController {
Java
복사
“api/v1/” 을 붙여줌으로써 api 기능을 한다는 암시를 준다.
API란?
1.
클라이언트가 서버에게 요청을 보냈을 때, 각자 다른 요청으로 보내면 서버가 알아듣기 어렵다.즉, '체계'가 필요하다.
a.
JSON 형식을 사용해서 모든 데이터를 표준화 한다
b.
❗️과거에는 'XML'이라는 형식이 널리 쓰였다.
2.
서로 다른 프로그램에서 요청과 응답을 주고 받을 수 있게 만든 체계이다
3.
API는 서버개발자가 개발하고, 클라이언트는 API를 사용한다.
4.
API를 만들 때는 데이터를 주고 받는 기능도 함께 넣는다. HospitalResponse(Dto)
→ API는 서버와 데이터베이스에 대한 출입구 역할을 한다.
JSON 형식
Javascript 객체 문법을 따르는 문자 기반의 데이터 포맷.
{
"id":"",
"roadNameAddress":"",
"hospitalName":"",
"patientRoomCount":"",
"totalNumberOfBeds":"",
"businessTypeName":"",
"totalAreaSize":""
}
Java
복사
hospitalresponse.json
HospitalRestController
@RestController // json형태로 제공하려면?
@RequestMapping("/api/v1/hospitals")
public class HospitalRestController {
private final HospitalRepository hospitalRepository;
public HospitalRestController(HospitalRepository hospitalRepository) {
this.hospitalRepository = hospitalRepository;
}
@GetMapping("/{id}")
// responseEntity<HospitalResponse>로 제공한다.
public ResponseEntity<HospitalResponse> get(@PathVariable Integer id){
Optional<Hospital> hospital = hospitalRepository.findById(id); //entity
return ResponseEntity.ok().body(Hospital.of(hospital.get())); // dto
// return은 dto로 한다.
}
}
Java
복사
레이어드 아키텍처 적용하기 이전 코드
2. 리팩토링 - 레이어드 아키텍처
•
현재 웹 어플리케이션 계층 구조
Controller
Repository
DB
Domain
•
레이어드 아키텍처 리팩토링 후 계층 구조
Controller
Service
Repository
DB
Domain(entity,HospitalResponse)
•
controller에서 repository(dao)를 직접 DI하고 있고, service 즉 비즈니스 계층이 없다.(좋은방향x)
•
service는 Presentation계층과 데이터 접근계층을 연결해주는 비즈니스 계층에 속하며, 사용자 관리 비즈니스 로직을 담을 클래스이다.
•
레이어드 아키텍처를 적용해보자
Presentation 계층 → Controller GET /api/v1/hospitals/{id}
비즈니스 계층 → service
데이터 접근 계층 → dao(Repository)
•
각 레이어는 가장 가까운 하위 레이어의 의존성을 주입받는다.
◦
controller에선 service를 사용하고,
◦
service는 repository를 사용한다.
◦
dao는 dao를(Repository JdbcTemplate) 사용
CODE
Service Layer에서 원하는 기능
•
business_status_code을 구분해서,
13일 경우 “영업중”, 3이면 “폐업”을 출력
13 - 영업중
24 - 직권폐업
3 - 폐업
2 - 휴업
HospitalResponse(Dto)
HospitalRestController(리팩토링)
Hospital
service
3. Controller의 TestCode 작성
테스트를 만들기 까다로운 이유
•
원인
◦
Controller에서 사용자의 Request를 받고 특정 Service를 호출해서 Return하니, 기능이 3개가 된다.
•
해결 방안
◦
비즈니스로직(Service)을 검증하지 않고 Controller만 검증한다.
◦
특정 Service를 호출해서 —> 이 기능 부분은 가짜 객체 Mock을 이용 합니다.
Service는 Repository를 호출합니다. 그래서 Service테스트를 만들때는 Repository는 Mock으로 처리 합니다.
HospitalRestControllerTest
@WebMvcTest(HospitalRestControllerTest.class) // 야근이 달린 문제
class HospitalRestControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
HospitalService hospitalService;
@Test
@DisplayName("1개의 Json형태로 Response가 잘 오는지") //비즈니스로직(Service를 검증하지 않음) Controller만 검증
void jsonResponse() throws Exception {
//{"id":2321,"roadNameAddress":"서울특별시 서초구 서초중앙로 230, 202호 (반포동, 동화반포프라자빌딩)","hospitalName":"노소아청소년과의원","patientRoomCount":0,"totalNumberOfBeds":0,
// "businessTypeName":"의원","totalAreaSize":0.0,"businessStatusName":"영업중"}
HospitalResponse hospitalResponse = HospitalResponse.builder()
.id(2321)
.roadNameAddress("서울특별시 서초구 서초중앙로 230, 202호 (반포동, 동화반포프라자빌딩)")
.hospitalName("노소아청소년과의원")
.patientRoomCount(0)
.totalNumberOfBeds(0)
.businessTypeName("의원")
.totalAreaSize(0.0f)
.businessStatusName("영업중")
.build();
given(hospitalService.getHospital(2321))
.willReturn(hospitalResponse);
int hospitalId = 2321;
// 앞에서 Autowired한 mockMvc
String url = String.format("/api/v1/hospitals/%d", hospitalId);
mockMvc.perform(get(url))
.andExpect(status().isOk())
.andExpect(jsonPath("$.hospitalName").exists()) // $는 루트 $아래에 hospitalName이 있어야 함
.andExpect(jsonPath("$.hospitalName").value("노소아청소년과의원"))
.andDo(print()); // http request, response내역을 출력 해라
verify(hospitalService).getHospital(hospitalId);// getHospital()메소드의 호출이 있었는지 확인
}
}
Java
복사
잘못된 클래스를 import할 경우 메소드가 사용되지 않기도 한다.
로컬에서 테스트 코드와 application이 잘 작동한다면, aws 서버를 띄워 동작시켜보자