REST Representational State Transfer
- REST란 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식이다.
- 자원을 나타내는 이름으로 구분하고 해당 자원의 상태(Representation of Resource)를 주고 받는 것을 의미한다.
- HTTP URI + HTTP Method
HTTP URI를 통해 제어할 자원을 명시하고 HTTP Method(GET, POST, PUT, DELETE ~CRUD Operation)를 통해 해당 자원을 제어하는 명령을 내리는 방식의 아키텍처
- 자원의 상태(실제 데이터)는 request body에 json이나 xml 형식으로 전달하는 것이 일반적이다.
특징
- 서버와 클라이언트의 역할을 명확하게 분리한다
Server - 자원을 가진 쪽, Client - 자원을 요청하는 쪽
- 무상태 Stateless
서버측에서 클라이언트의 context를 유지할 필요 없다. 각각의 요청은 완전히 별개의 것으로 인식.
- 인터페이스의 일관성 Uniform Interface
HTTP 표준 프로토콜을 따르는 모든 플랫폼에서 사용 가능
- 캐시 가능 Cacheable
- 계층형 구조 Layered System
다중 계층으로 구성될 수 있다.
장점
- 웹과 http 프로토콜을 사용하므로 범용적이고 별도의 인프라 구축이 필요없다.
- 요청이 의도하는 바를 명확하게 나타낸다. Self-Descriptive
단점
- REST는 설계 가이드일 뿐 별도의 표준이 존재하지 않는다.
- HTTP Method가 제한적이다
Rest API
REST 기반으로 서비스 API를 구현 한 것
**API Application Programming Interface : 데이터와 기능의 집합을 제공하여 프로그램들이 서로 상호작용하는 것을 도와주는 매개체. 타인이 만든 프로그램의 동작이나 내부를 몰라도 활용할 수 있게 해주는 중간 매개체
Rest API 설계
- URI는 자원(resource)을 표현해야 한다.
- 동사 대신 명사, 대문자 대신 소문자 사용
- 복수명사 사용
- 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE 등)로 표현
- URI에 행위에 대한 동사 표현이 들어가면 안된다.
- 슬래시(/)는 계층 관계를 나타내는데 사용한다.
- URI 끝에 슬래시(/)를 포함하지 않는다.
- 밑줄(_)은 사용하지 않는다.
- 하이픈(-)은 가독성을 높이는데 사용한다.
- 파일 확장자는URI에 포함하지 않는다. 대신 Accept header로 표시
RestController
스프링에서 REST API를 만들기 위해 사용하는 컨트롤러 어노테이션.
기존 Controller는 주로 ModelAndView를 통해 view를 응답한다.
반면, RestController는 데이터를 응답한다.
@RestController
= @Controller + @ResponseBody
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
public class MemberController {
//private final MemberDao memberDao;
private final MemberService memberService;
private final MemberMapper memberMapper;
@GetMapping()
public List<Member> getMemberList(){
return memberMapper.findAll();
}
@GetMapping("/{memberId}")
public Member getMember(@PathVariable Long memberId){
return memberMapper.findOneById(memberId);
}
@PostMapping()
public Long insertMember(@RequestBody Member member){
memberMapper.save(member);
return member.getMemberId();
}
@PutMapping("/{memberId}")
public void updateMember(@PathVariable Long memberId, @RequestBody Member member){
member.setMemberId(memberId);
memberMapper.update(member);
}
@DeleteMapping("/{memberId}")
public void deleteMember(@PathVariable Long memberId){
memberService.delete(memberId);
}
}
|
cs |
기존 URI 와 RESTful URI 비교
기능 | method | restful URL | 기존 URI |
---|---|---|---|
회원 등록 | POST | /member | /member/insertMember |
회원 목록 | GET | /member | /member/getMemberList |
회원 상세 | GET | /member/{memberId} | /member/getMemberDetail |
회원 수정 | PUT | /member/{memberId} | /member/updateMember |
회원 삭제 | DELETE | /member/{memberId} | /member/deleteMember |
기존 URI는 규격이 명확하지 않아서 표현이 다양하기 때문에 사용하거나 협업하기에 불편하다
Springboot - Controller Unit test
@SpringBootTest
슬라이싱하지 않고 전체 application context를 구성하기 때문에 전체 통합 테스트를 하기에 적합하다.
@WebMvcTest
- Controller(Web) layer를 테스트 할 때 사용-Sliced test
- @Controller, @ControllerAdvice, @JsonComponent와 Filter, WebMvcConfiguer, HandlerMethodArgumentResolver만 로드
이 외에 필요한 Bean을 직접 로드해줘야 한다. → @Import(*Service.class)
- @MockBean
MockBean 어노테이션을 붙이면 가짜 객체를 만들어서 주입해준다. 해당 객체의 내부의 동작들은 실행되지 않는다.
- Mybatis를 사용한다면 @MybatisTest 대신 @AutoConfigureMybatis를 선언해야한다.
이렇게하면 mapper가 빈으로 등록되고 알아서 주입된다
음 근데 이정도로 설정 할거면 그냥 springboottest+autoConfigureMockMvc 로 돌리는게 나을것 같다는 생각이든다. controller 에 서비스 처리가 정상적으로 이뤄져있는지 확인하니까 단위테스트가 아닌 느낌.. 그럼 컨트롤러 단위 테스트는 무슨 용도로 사용하는지 좀 더 찾아봐야 할듯. 우선은 @WebMvcTest로 테스트 코드를 작성했다.
MockMvc
스프링 mvc를 테스트 하기 위해 사용하는 것으로 ajax나 client(browser)의 요청을 컨트롤러가 받아 처리하는 것과 같은 테스트를 진행할 수 있다. 실제 객체와 비슷하지만 테스트에 필요한 기능만 가지는 가짜 객체를 만들어서 애플리케이션 서버에 배포하지 않고도 스프링 MVC 동작을 재현할 수 있다. spring-boot-test에 기본 포함
- perform() : 요청을 전송. 결과를 검증할 수 있는 ResultActions 객체를 리턴 받는다
- get("URI") : HTTP 메소드 결정, 파라미터는 URI
- params() : MultiValueMap<String,String>으로 파라미터 값을 전달한다 ~ @RequestParam
- content() : body안에 데이터를 담아 전달한다.
- objectMapper를 통해 객체를 바이트로 변환해서 전달한다.
이때 writeValueAsBytes 메서드로는 값이 정상적으로 전달 되는데
writeValueAsString 메서드로 하면 제대로 전달되지 않는다. 왜인지는.. 알게되면 알려주세요
- objectMapper를 통해 객체를 바이트로 변환해서 전달한다.
- contentType() : header의 content-type 설정
- andDo(print()) : 요청과 응답의 전체 메세지를 확인할 수 있다.
- andExpect() : 응답을 검증하는 역할
- status() 상태코드 검증
- isOk() : 200
- isNotFound() : 404
- isMethodNotAllowed() : 405
- content() 응답 검증
- string() 응답 메세지 비교
- jsonPath() json 객체의 값을 비교할 수 있습니다
- status() 상태코드 검증
아래 코드를 보시고 눈치껏 쓰거나 필요한 것은 그때그때 검색해서 추가하면 될 듯 합니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@RunWith(SpringRunner.class)
@WebMvcTest({MemberController.class})
@AutoConfigureMybatis
@Import(MemberService.class)
public class MemberControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private MemberMapper memberMapper;
//이렇게 선언하면 controller 내부의 services는 모키토로 만들어진 빈을 주입받음
//@MockBean
//private MemberService memberService;
@Test
public void joinMember() throws Exception {
Member member = new Member("fine","ty","1111");
//when
ResultActions actions =
mockMvc.perform(post("/member")
.contentType("application/json")
.content(objectMapper.writeValueAsBytes(member)))
.andDo(print());
Member m = memberMapper.findOneByUserId("fine");
//then
actions.andExpect(status().isOk())
.andExpect(content().string(m.getMemberId().toString()));
}
@Test
public void getMemberList_test() throws Exception {
//when
ResultActions actions = mockMvc.perform(get("/member"))
.andDo(print());
//then
actions.andExpect(status().isOk())
.andExpect(jsonPath("$[*]",hasSize(2)))
.andExpect(jsonPath("$[0].userId",is("zero")))
.andExpect(jsonPath("$[0].name",is("taeyeon")))
.andExpect(jsonPath("$[1].userId",is("spark")));
}
}
|
cs |
다음 포스팅은 spring에서 http 통신으로 외부의 api를 호출하는 내용으로 돌아오겠습니다.
'web > springboot' 카테고리의 다른 글
[springboot] RestTemplate (RestTemplate 기초, RestTemplate으로 카카오 API 호출하기) (0) | 2020.11.26 |
---|---|
[springboot] mybatis 사용하기(+mybatis unit test) (2) | 2020.11.20 |
[springboot] springboot 특징(의존성 관리, Auto Configuration) (0) | 2020.11.16 |
댓글