Web/Spring 2018. 9. 30. 20:33

저와 같은 바보같은 분은 없을 것이라고 생각이 들지만 제가 겪었던 하나의 문제였기 때문에 작성해봅니다....


만약 @Controller 클래스에서 요청바디에 넘어온 데이터를 받기위하여 @RequestBody라는 어노테이션을 다들 많이 쓰실 겁니다. 다른 분들은 절대 저와 같은 바보같은 짓을 하시지는 않으셨겠지만, 저는 요청 바디로 넘어온 데이터를 적절히 2개의 객체로 나누기 위해서 메소드 파라미터에 2개의 @RequestBody를 사용하였습니다. 근데 이게 뭐람 에러 뚜뚱... 에러가 발생하였습니다. 무슨 에러인가 막 찾아보고 고민하던 중에 중요한 사실을 알게되었습니다. 

요청 바디로 넘어온 데이터를 받기위해 @RequestBody를 사용하는데 이 어노테이션은 스트림을 이용하여 요청바디의 데이터를 불러 온다는 것... 한번 열어서 사용한 스트림은 재사용이 불가능 하기 때문에 첫번째 @RequestBody로 받고 난 뒤, 다시 @RequestBody로는 당연히 받을 수 없는 것입니다... 혹시라도 저와 같은 분은 없을 것이라고 생각하지만...있으시다면 참고바라면서 작성하였습니다.


만약 2개의 객체의 요청바디의 데이터를 적절히 나눌 필요가 있다면 하나의 객체로 모든 데이터를 다 받아와서 그 객체를 이용해 다른 객체들에게 데이터를 나눠 담는 방법을 이용하시면 됩니다..




@RequestMapping("abc")

public String controllerMethod(@RequestBody A a,@RequestBody B b) {......} => 절대 안됩니다...




posted by 여성게
:
Web/Spring 2018. 9. 20. 23:22

모든 컨트롤러의 중복된 로직 코드량 확줄여버리는 코딩전략, 제네릭스와 매핑정보 상속을 이용한 컨트롤러 작성전략




웹개발을 하면서 지겹도록 작성하는 코드가 CRUD 관련 코드이다. 매번 똑같은 패턴이며 도메인 오브젝트만 바뀔뿐 딱히 로직이 크게 차이가 없다. 그렇지만 우리는 서비스 클래스는 물론 컨트롤러의 코드에 CRUD코드가 많이 중복되며 대부분 서비스클래스에게 작업을 위임해주는 작업뿐이다. 그렇다면 어떻게 중복된 것을 최대한 배제시킬 수 있을까?

그것이 바로 제네릭스를 이용한 상속 전략이다. 바로 예제 코드로 넘어가면,


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
package com.toby.spring.controller;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
public abstract class GenericController<D,P,S> {
    S service;
    @RequestMapping(method=RequestMethod.POST)
    public int add(D dto) {
        System.out.println("DTO 객체에 대한 DAO 삽입로직");
        return 0;
    }
    @RequestMapping(method=RequestMethod.PUT)
    public int update(D dto) {
        System.out.println("DTO 객체에 대한 DAO 수정로직");
        return 1;
    }
    @RequestMapping(value="{id}",method=RequestMethod.GET)
    public D getByID(P primaryKey) {
        System.out.println("id 값을 이용하여 DTO 객체를 가져오는 DAO 로직");
        D d=(D) new Object();
        return d;
    }
    @RequestMapping(method=RequestMethod.DELETE)
    public int delete(P primaryKey) {
        System.out.println("id 값을 이용하여 해당 DTO 객체를 삭제하는 DAO 로직");
        return 1;
    }
    @RequestMapping(value="/all",method=RequestMethod.GET)
    public List<D> getByList(){
        System.out.println("DAO에서 DTO 타입의 객체 리스트를 가져오는 로직");
        List<D> dList=new ArrayList();
        return dList;
    }
}
 
cs


이것은 대부분의 컨트롤러에서 중복되는 CRUD관련 공통적인 컨트롤러 메소드 및 매핑선언이다. 여기서 집중해서 봐야하는 것이 제네릭스 타입이다. 물론 이것은 예제 코드라서 간단한 제네릭스 타입이지만(실무코드에서는 조금더 다듬어져야하는 부분) 대게 저렇게 3가지 타입으로 컨트롤러 단에서 CRUD 로직을 처리가능하다. 또하나 집중해서 봐야하는 것은 추상클래스의 메소드의 @RequestMapping이다. 이거 관련해서는 뒤에서 설명한다. 이 추상클래스를 상속하는 실 컨트롤러 클래스를 만들어준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.toby.spring.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.toby.spring.dto.UserDTO;
import com.toby.spring.service.UserService;
 
@Controller
@RequestMapping("/user")
public class UserController extends GenericController<UserDTO, Integer, UserService>{
    @RequestMapping("/login")
    public void login(UserDTO userDto) {
        System.out.println("로그인 인증관련 로직");
    }
    @RequestMapping("/logout")
    public void logout() {
        System.out.println("로그아웃 관련 로직");
    }
}
 
cs


너무 간단하지 않은가? CRUD 관련 메소드는 보이지도 않으면서 메소드를 이용할 수 있다. 상속받은 컨트롤러 제네릭스 타입에 집중하자 그러면 추상클래스의 제네릭스 타입들에 들어가는 타입을 알 수 있을 것이며, 이것들은 많이 봐왔던 패턴일 것이다. 그리고 중요한 것이 실 컨트롤러 클래스 타입에 공통적인 url패턴을 등록하고 각각 CRUD 로직의 매핑은 추상클래스의 매핑타입을 그대로 상속받아 사용하는 것이다. 그렇다면 여타 게시판관련 CRUD에서는 똑같이 추상클래스를 상속하고 단순히 클래스 타입에 @RequestMapping url값만 바꿔주면 그대로 사용이 가능한 것이다. 물론 CRUD 이외의 매핑과 메소드는 실 컨트롤러 클래스에서 선언하여 사용할 수 있다. 이렇게 제네릭스 타입을 이용하여 추상클래스를 만들면 여타 엄청난 양의 코드가 줄어버린다. 지금은 컨트롤러에서만 이 전략을 이용하였지만 추후에 서비스클래스 쪽과 DAO쪽도 이와 같은 전략을 이용한다면 전체적으로 일관된 프로젝트 전략이 될것이며 덩달아 코드량도 엄청나게 줄어버릴 것이다. 



posted by 여성게
: