JSON(JavaScript Object Notation) : 자바스크립트 객체 표기업
{"이름":"값", "이름":"값",...}
Rest (RepresEntational State Transfer) : 표현적 상태 전이
GET /api/member/list - 조회 상태 URL
POST /api/member
jackson-databind
jackson-datatype-jsr310 - Data & Time API - java.time 패키지
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2' //Jackson Databind » 2.17.2
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2' //Jackson Datatype: JSR310 » 2.17.2
ObjectMapper 클래스
writeObjectAsString(자바객체) : 자바객체 👉 JSON문자열readValue(...) : JSON문자열 👉 자바객체반환값
@Slf4j
@RestController
@RequestMapping("/api/member")
@RequiredArgsConstructor
public class ApiMemberController {
private final MemberMapper mapper;
@GetMapping("/info/{email}")
public Member info(@PathVariable("email") String email){
//content-Type : application/json
Member member = mapper.get(email);
return member;
}
@GetMapping("/list") //리스트형
public List<Member> list(){
List<Member> members = IntStream.rangeClosed(1,10)
.mapToObj(i -> Member.builder()
.email("user" + i + "test.org")
.password("12345678")
.userName("사용자" + i)
.regDt(LocalDateTime.now())
.build())
.toList();
return members;
}
@GetMapping(path="/test", produces = "text/html;charset=UTF-8")
public String test(){
//content-Type : text/palin
return "ㅎㅇ";
}
@GetMapping("/test2")
public void test2(){
log.info("test2");
}
}


@Controller로 설정된 일반 컨트롤러 메서드를 Rest로 응답 하게 만들어주는 애노테이션
자바 객체, 문자열, 반환값 없음
MemberController
@ResponseBody
@GetMapping("/list")
public List<Member> list(){
List<Member> members = IntStream.rangeClosed(1,10)
.mapToObj(i -> Member.builder()
.email("user" + i + "test.org")
.password("12345678")
.userName("사용자" + i)
.regDt(LocalDateTime.now())
.build())
.toList();
return members;
}
JSON 변환시 제외
👉 민감한 정보 예) 비밀번호 등..
@Data
@Builder
@NoArgsConstructor @AllArgsConstructor
//@Table
public class Member {
@Id //기본키 설정
@Column("ID")
private Long seq;
private String email;
@JsonIgnore
private String password;
private String userName;
private LocalDateTime regDt;
}

@Data
@Builder
@NoArgsConstructor @AllArgsConstructor
//@Table
public class Member {
@Id //기본키 설정
@Column("ID")
private Long seq;
private String email;
@JsonIgnore
private String password;
private String userName;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime regDt;
}
MvcConfig
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
ObjectMapper mapper = Jackson2ObjectMapperBuilder
.json()
.serializerByType(LocalDateTime.class,new LocalDateSerializer(formatter))
.build();
converters.add(new MappingJackson2HttpMessageConverter(mapper)); //가장 먼저 적용될수있도록
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
ObjectMapper mapper = Jackson2ObjectMapperBuilder
.xml()
.serializerByType(LocalDateTime.class,new LocalDateSerializer(formatter))
.build();
converters.add(new MappingJackson2HttpMessageConverter(mapper)); //가장 먼저 적용될수있도록
}
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.2' //Jackson Dataformat XML » 2.17.2
커맨드 객체 변환 기준 - Content-Type:application/x-www.form-urlencoded;
커맨드객체 앞에 @RequestBody를 추가하면 Content-Type:application/json으로 판단하고 데이터 변환
POST
PUT
PATCH
POSTMAN : REST 테스트
ARC(Advanced Rest Client) : REST 테스트
MockMvc :
객체 리턴하고 응답 코드 지정
응답 헤더, 바디쪽을 상세하게 설정하는 경우
응답 상태 코드 + 출력 데이터
ApiMemberController
@GetMapping("/list")
public ResponseEntity<List<Member>> list(){
List<Member> members = IntStream.rangeClosed(1,10)
.mapToObj(i -> Member.builder()
.email("user" + i + "test.org")
.password("12345678")
.userName("사용자" + i)
.regDt(LocalDateTime.now())
.build())
.toList();
return ResponseEntity.status(HttpStatus.OK).body(members);
}
Test
@Test
void test2() throws Exception{
mockMvc.perform(get("/api/member/list"))
.andDo(print());
}
응답 상태 코드 / 출력 데이터 ❌
ApiMemberController
@PostMapping()
public ResponseEntity join(@RequestBody RequestJoin form){
joinService.process(form);
//응답코드 201, 출력바디 x
return ResponseEntity.status(HttpStatus.CREATED).build();
}
Test
@Test
void test1() throws Exception{
ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
RequestJoin form = new RequestJoin();
form.setEmail("user99@test.org");
form.setPassword("12345678");
form.setConfirmPassword("12345678");
form.setUserName("사용자99");
form.setAgree(true);
String json = om.writeValueAsString(form);
mockMvc.perform(
post("/api/member")
.contentType(MediaType.APPLICATION_JSON) //요청헤드
.content(json) //요청바디
).andDo(print()).andExpect(status().isCreated());
ApiMemberController
@GetMapping("/info/{email}")
public ResponseEntity<Member> info(@PathVariable("email") String email){
//content-Type : application/json
Member member = mapper.get(email);
return ResponseEntity.ok(member);
}
JSONData
@Data
@NoArgsConstructor
@RequiredArgsConstructor
public class JSONData {
private HttpStatus status = HttpStatus.OK; //상태코드, 기본값 200, 성공이 많고 간혹 실패하기 때문에
private boolean success = true; //성공여부, 기본값 true, 성공이 많고 간혹실패하기 떄문에
private String message; //실패시 메세지
@NonNull
private Object data; //성공시 데이터
}
@SpringJUnitWebConfig
@ContextConfiguration(classes = MvcConfig.class)
public class Ex01 {
@Test
void test1(){
UriComponents url = UriComponentsBuilder
.fromUriString("https://www.naver.com")
.path("/news")
.queryParam("t1","v1")
.queryParam("t2","v2")
.queryParam("t3","한글")
.fragment("hash")
.encode()
.build();
System.out.println(url.toUriString());
}
}
//>>https://www.naver.com/news?t1=v1&t2=v2&t3=%ED%95%9C%EA%B8%80#hash
build(Object... uriVariables) : 반환값 URI, 인스턴스를 빌드 URI하고 URI 템플릿 변수를 배열 값으로 바꿉니다.
@SpringJUnitWebConfig
@ContextConfiguration(classes = MvcConfig.class)
public class Ex01 {
@Test
void test1(){
URI url = UriComponentsBuilder
.fromUriString("https://www.naver.com")
.path("/news/{0}") //{0} = uri변수
.queryParam("t1","v1")
.queryParam("t2","v2")
.queryParam("t3","한글")
.queryParam("t4","aa{1}")
.fragment("hash")
.encode()
.build("AAAA","BBBB");
System.out.println(url);
}
}
//>>https://www.naver.com/news/AAAA?t1=v1&t2=v2&t3=%ED%95%9C%EA%B8%80&t4=aaBBBB#hash
<T> ResponseEntity<T> getForEntity(...)<T> T getForObject<T> ResponseEntity<T> postForEntity<T> T postForObject<T> ResponseEntity<T> exchange(...)HttpEntity - 헤더와 바디 함께 전송
@Data
public class PostData {
private long userId;
private long id;
private String title;
private String body;
}
@Test
void test3() throws Exception{
RestTemplate restTemplate = new RestTemplate();
String body = restTemplate.getForObject("https://jsonplaceholder.typicode.com/posts/1", String.class);
// 단일 객체 변환
PostData data = om.readValue(body, PostData.class);
System.out.println(data);
//복합 데이터 객체 변환 - List, Set, Mao..
String itemsBody = restTemplate.getForObject("https://jsonplaceholder.typicode.com/posts/", String.class);
List<PostData> items = om.readValue(itemsBody, new TypeReference<List<PostData>>(){});
items.forEach(System.out::println);
}
getFieldErrors() : 필드별 전체 에러 정보
getGlobalErrors() : 커맨드 객체 자체 에러 정보 (reject(...) ...)
getAllErrors() : 전체 에러 정보