웹에서 보낸 Request Parameter를 Controller 에서 처리해야 하는데
같은 로직에 다양한 VO / DTO 를 넣어줘야 할 경우가 있다.
VO/DTO 마다 Controller를 새로 구현하여 데이터를 수신받는 것은 중복된 코드가 많아지고 유지보수도 귀찮아진다.
필드상에 VO/DTO를 선언하지 않고 하나의 Controller 에서 데이터를 수신할 수 있는 방법 2가지와
수신받은 데이터를 VO / DTO에 매핑시킬 수 있게 ObjectMapper 라이브러리를 사용한 방식에 대해 작성하였다.
매핑시킬 VO
@Data
public class Person {
private int age;
private String name;
private String birth;
}
서버로 데이터 전송
모든 Parameter를 HashMap 으로 받기
JavaScript
<body>
<button onclick="submitHandler()">SUBMIT</button>
</body>
const submitHandler = () => {
let formData = new FormData();
formData.append("dtoName", "Person"); // DTO 클래스 명
formData.append("age", "26"); // DTO 멤버변수
formData.append("name", "kdh");
formData.append("birth", "971001");
// formData 값 확인
console.log(...formData);
fetch('http://localhost:9003/input', {
method: 'POST',
cache: 'no-cache',
body: formData // body 부분에 폼데이터 변수를 할당
})
.then((response) => response.json())
.then((data) => {
if(data.result === 1) {
console.log(data);
}
else {
console.log("요청 실패");
}
});
}
dto 의 클래스명과 각 멤버변수의 값을 formData에 넣고 넘겨준다.
Controller
@RestController
@RequiredArgsConstructor // lombok
public class Controller {
public final DtoMappingService dtoMappingService;
@PostMapping("/input")
@ResponseBody
public ResponseEntity<?> dtoMapping( @RequestParam HashMap<String, Object> item ) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
dtoMappingService.dtoMapping( item );
if( )
map.put("result", 1);
else
map.put("result", 0);
return new ResponseEntity<Map<String, Object>>(map, HttpStatus.OK);
}
@RequestParam 어노테이션 안에 아무런 명칭을 지정하지 않고 HashMap을 사용하면
모든 데이터가 key:value 형태로 들어가게 된다.
Service
@Service
public class DtoMappingService{
public void dtoMapping(HashMap<String, Object> item) {
List<RuleItemDto> resultList = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
String dtoName = item.getKey("dtoName");
item.remove("dtoName"); // Object Mapping 시 없는 멤버변수라서 지워야 함
try
{
Object item = mapper.convertValue(item, Class.forName("패키지 경로." + dtoName ).newInstance().getClass());
catch ( ClassNotFoundException cnfe ) {
System.out.println( cnfe );
}
}
Dto 내 Map에 매핑
javascript
<body>
<button onclick="submitHandler()">SUBMIT</button>
</body>
const submitHandler = () => {
let formData = new FormData();
formData.append("dtoName", "Person"); // DTO 클래스 명
formData.append("item[age]", "26"); // DTO 멤버변수
formData.append("item[name]", "kdh");
formData.append("item[birth]", "971001");
// formData 값 확인
console.log(...formData);
fetch('http://localhost:9003/input', {
method: 'POST',
cache: 'no-cache',
body: formData // body 부분에 폼데이터 변수를 할당
})
.then((response) => response.json())
.then((data) => {
if(data.result === 1) {
console.log(data);
}
else {
console.log("요청 실패");
}
});
}
formData.append 함수의 name 부분을 "map변수명[key]" 로 입력 시 Map 의 key 로 들어가게 된다.
Dto
@Data // lombok
public class InputDto{
private String dtoName;
private HashMap<String, Object> item;
}
Controller
@RestController
@RequiredArgsConstructor // lombok
public class Controller {
public final DtoMappingService dtoMappingService;
@PostMapping("/input")
public ResponseEntity<?> dtoMapping( InputDto dto ) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
dtoMappingService.dtoMapping( dto );
if( 로직 성공 시 )
map.put("result", 1);
else
map.put("result", 0);
return new ResponseEntity<Map<String, Object>>(map, HttpStatus.OK);
}
Controller 의 매개변수 부분에 Dto를 넣어주면 formData 의 name 에 알아서 맞춰서 들어가진다.
Service
@Service
public class DtoMappingService{
public void dtoMapping( InputDto item) {
List<RuleItemDto> resultList = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
try
{
Object item = mapper.convertValue(dto.getItem(), Class.forName("패키지 경로." + dto.getDtoName() ).newInstance().getClass());
catch ( ClassNotFoundException cnfe ) {
System.out.println( cnfe );
}
}
1)은 모든 Parameter를 HashMap으로 받아서 dtoName을 제거해줘야 했지만
원하는 값만 HashMap으로 받았기 때문에 1번 처럼 dtoName을 제거하는 과정이 필요가 없어진다.
Dto 내 List에 매핑
formData를 받을 DTO를 생성한 후 formData에서 DTO의 멤버변수에 맞게 삽입해준다.
위 방법들의 단점과는 다르게 원하는 값만 List안에 넣을 수 있고, json으로 파싱하지 않아도 된다.
javascript
const submitButton = document.getElementById("submitButton");
function submit () {
let formData = new FormData();
formData.append("dtoName", "Person"); // DTO 클래스 명
itemList.forEach((item, index) => {
formData.append("itemLiet[" + i + "].key", item);
formData.append("itemLiet[" + i + "].value", document.getElementById(item).value);
});
// formData 값 확인
console.log(...formData);
fetch('http://localhost:9003/input', {
method: 'POST',
cache: 'no-cache',
body: formData // body 부분에 폼데이터 변수를 할당
})
.then((response) => response.json())
.then((data) => {
if(data.result === 1) {
console.log(data);
}
else {
console.log("요청 실패");
}
});
}
submitButton.addEventListener("click", submit);
age, name, birth를 갖는 itemList와 그걸 id로 갖는 HTML Input Element가 있다고 치자..
DTO
@Data // lombok
public class ItemDto{
private String key;
private Object value;
}
JSON 배열을 받을 수 있는 Dto를 생성해준다.
( VO에 매핑시킬 목적이 아니라면 꼭 key value 형식일 필요는 없다. )
@Data // lombok
public class InputDto {
private String dtoName;
private List<ItemDto> itemList;
}
Controller
@RestController
@RequiredArgsConstructor // lombok
public class Controller {
public final DtoMappingService dtoMappingService;
@PostMapping("/input")
@ResponseBody
public ResponseEntity<?> dtoMapping( InputDto dto ) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
dtoMappingService.dtoMapping( dto );
if( 로직 성공 시 )
map.put("result", 1);
else
map.put("result", 0);
return new ResponseEntity<Map<String, Object>>(map, HttpStatus.OK);
}
Service
( VO 매핑이 굳이 필요 없다면 그냥 List 가져가서 사용해도 된다. )
@Service
public class DtoMappingService{
public void dtoMapping( InputDto dto ) {
List<RuleItemDto> resultList = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
Map<String ,Object> map = new HashMap<>();
for (ItemDto itemDto : dto.getItemList())
map.put(itemDto.getKey(), itemDto.getValue());
try
{
Object item = mapper.convertValue(dto.getItem(), Class.forName("패키지 경로." + dto.getDtoName() ).newInstance().getClass());
catch ( ClassNotFoundException cnfe ) {
System.out.println( cnfe );
}
}
json 으로 파싱하여 전송
1) 과의 차이점은 모든 데이터가 HashMap으로 구분없이 들어가지만
json으로 전송 시 json의 Key 값을 이용해 DTO에 매핑시킬 수 있다.
대신 단점으로는 json 으로 파싱하는 과정이 추가된다.
JavaScript
const submitButton = document.getElementById("submitButton");
function submit () {
let jsonData = {
"dtoName": "Person",
"item": {
"age":"26"
"name":"kdh"
"birth":"971001"
};
fetch('http://localhost:9003/input', {
method: 'POST',
cache: 'no-cache',
headers: {
"Content-Type" : "application/json" // 기본 타입이 x-www.form-urlencoded 라서 바꿔줘야 함
},
body: JSON.stringfiy( jsonData ) // json 형식 데이터를 문자열로 변환
})
.then((response) => response.json())
.then((data) => {
if(data.result === 1) {
console.log(data);
}
else {
console.log("요청 실패");
}
});
}
submitButton.addEventListener("click", submit);
Dto
@Data // lombok
public class InputDto{
private String dtoName;
private Object item; // json 배열. HashMap도 가능
}
변수명은 json의 key 와 일치하게 작성해준다.
HashMap 으로 하면 json 배열의 key/value 가 HashMap의 key/value로 삽입된다.
Controller
@RestController
@RequiredArgsConstructor // lombok
public class Controller {
public final DtoMappingService dtoMappingService;
@PostMapping("/input")
public ResponseEntity<?> dtoMapping( @RequestBody InputDto dto ) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
dtoMappingService.dtoMapping( dto );
if( 로직 성공 시 )
map.put("result", 1);
else
map.put("result", 0);
return new ResponseEntity<Map<String, Object>>(map, HttpStatus.OK);
}
Service
@Service
public class DtoMappingService{
public void dtoMapping( InputDto item) {
List<RuleItemDto> resultList = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
try
{
Object item = mapper.convertValue(dto.getItem(), Class.forName("패키지 경로." + dto.getDtoName() ).newInstance().getClass());
catch ( ClassNotFoundException cnfe ) {
System.out.println( cnfe );
}
}
매핑 된 값 추출
이 후 공통 로직을 구현할 때는 Field 클래스를 사용하여 값을 추출해내면 됩니다.
특정 멤버변수
Field field = ReflectionUtils.findField(item.getClass(), 멤버변수명);
Objects.requireNonNull(field).setAccessible(true); // private 접근 허용
String key = field.getName(); // 변수명
Object value = field.get(item).toString(); // 값
모든 멤버변수
for (Field field : item.getClass().getDeclaredFields()){
field.setAccessible(true);
field.getName(); // 멤버변수명 return
field.get(item).toString(); // 멤버변수 값 return
}
'Back-End > Java & Spring' 카테고리의 다른 글
[JAVA] 특정 클래스를 상속받은 모든 클래스를 찾는 방법 (0) | 2022.08.17 |
---|---|
[JAVA] 제네릭 표현 (0) | 2022.08.17 |
[JAVA] Log4j (0) | 2022.08.02 |
[JAVA] JNDI( Java Naming and Directory Interface ) (0) | 2022.08.02 |
[JAVA] Apach Axiom API (XML Parser) (0) | 2022.08.02 |