본문 바로가기
Backend 개발자/StackOverflow

Rest Api 개발할때 @Setter 안쓰고 조회기능 만들기 Get Post 언제 상황에 맞게 써야하나요?

by by 앵과장 2022. 5. 26.
반응형

안녕하세요
앵과장입니다.

해당 내용은 개발하면서 지극히 주관적인 내용을 정리라는 내용이라는 점 참고하세요


 

Rest Api 개발할 때 Get Post
언제 별도로 사용해야 하나요?

최근 개발하는 프로젝트가 백앤드 개발이긴 한데 클라이언트 개발자가 사용하는 BFF레이어 개발을 주로 진행을 하다 보니

언제 Get을 쓰고 Post는 언제 사용해야 되는지에 대해서 고민을 하게 됩니다.


Get 방식


고민을 했던 기능에 대해서 예시를 들어보도록 하겠습니다.

30개 정도의 메뉴가 비슷한 유형에 조회 기능입니다.
Client에서 호출할 때 필수(Required) 또는 제외 가능(Option)입니다.

각기능에서 요청에 필요한 Request 파라미터는 1개~40개 정도까지 다양합니다.

초반 개발할 때는 Get방식으로 구현을 진행하면서 Request 처리하는 방식 중 DTO처리 시 Setter는 사용하지 않는 방향으로 진행하였습니다.

Dto 처리할 때 Get 방식 중 Setter를 사용하지 않기 위해서는
Builder 패턴을 사용하는 방법이 나 정적 메서드를 이용해서
Sevice 레이어에 전달하는 2가지 방식을 사용하였습니다.

@GetMapping("/generals/information")
public ResponseEntity<BffResponse> getInformation(@RequestParam Information information){
	return ResponseEntity.ok(informationService.getinformation(information));
	public ResponseEntity<EveryonesBffResponse> getEveryonesRecruitInformation(
		@RequestParam(value="area", required = false) String area,
		@RequestParam(value="part", required = false) String part,
		@RequestParam(value="workPeriod", required = false) String workPeriod,
		@RequestParam(value="workdays", required = false) String workdays,
		@RequestParam(value="workhour", required = false) String workhour,
		@RequestParam(value="gender", required = false) String gender,
		@RequestParam(value="salary", required = false) String salary,
		@RequestParam(value="employmentType", required = false) String employmentType,
		@RequestParam(value="educationLevel", required = false) String educationLevel,
		@RequestParam(value="welfareBenefits", required = false) String welfareBenefits,
		@RequestParam(value="preferentialTerms", required = false) String preferentialTerms,
		@RequestParam(value="acceptanceMethod", required = false) String acceptanceMethod,
		@RequestParam(value="searchPeriod", required = false) String searchPeriod,
		@RequestParam(value="pageIndex", required = false) int pageIndex,
		@RequestParam(value="pageRowSize", required = false) int pageRowSize
	){
	return ResponseEntity.ok(informationService
			.getInformation(
			Information.ofInformation(
				area,part,workPeriod,workdays,
				workhour,gender,salary,
				employmentType,educationLevel,
				welfareBenefits, preferentialTerms, acceptanceMethod,
				searchPeriod, pageIndex, pageRowSize
			)
    ));
}

Get방식으로 처리하고 Setter 안 쓰고 하려고 하니

명확하게 어떤 파라미터를 사용하게 될지 Swagger UI나 클라이언트에게는 전달이 되는데

개발하는 데 있어서 Syntax도 길어지고 리소스 비용이 상당히 증가하게 되고 

개발하고 있는 단계이다 보니 필드명 변경도 발생하고 

파라미터가 많아지다 보니 QueryString 길이 제한에 대한 고민과 이게 맞는 게 아닌 거 같단 생각이 들어서 POST방식으로 변경하게 되었습니다.

 

 

Post 방식

개발이 진행되고 조회 기능에서 사용하는 Request 방식 중 사용해야 하는
DTO레이어가 공통으로 사용할 수 있는 필드들이 어느 정도 도출되면서

초반에 Get으로 진행한 방식에서 Post로 변경이 필요한 시기가 되었습니다.
30 , 40개의 파라미터 필드들을 QueryString 방식으로 처리하기보다는 Json으로 받아서 처리하는 게 좀 더 적합해 보였고

QueryString정보는 클라이언트에서는 Uri에 노출되기도 하지만 길이에 대한 제한도 존재합니다.

QueryString 브라우저별 길이 제한

Microsoft Internet Explorer (Browser)
Microsoft states that the maximum length of a URL in Internet Explorer is 2,083 characters, with no more than 2,048 characters in the path portion of the URL. Attempts to use URLs longer than this produced a clear error message in Internet Explorer.

Microsoft Edge (Browser)
The limit appears to be around 81578 characters. See URL Length limitation of Microsoft Edge

Chrome
It stops displaying the URL after 64k characters, but can serve more than 100k characters. No further testing was done beyond that.

Firefox (Browser)
After 65,536 characters, the location bar no longer displays the URL in Windows Firefox 1.5.x. However, longer URLs will work. No further testing was done after 100,000 characters.

Safari (Browser)
At least 80,000 characters will work. Testing was not tried beyond that.

Opera (Browser)
At least 190,000 characters will work. Stopped testing after 190,000 characters. Opera 9 for Windows continued to display a fully editable, copyable and pasteable URL in the location bar even at 190,000 characters.

Apache (Server)
Early attempts to measure the maximum URL length in web browsers bumped into a server URL length limit of approximately 4,000 characters, after which Apache produces a "413 Entity Too Large" error. The current up to date Apache build found in Red Hat Enterprise Linux 4 was used. The official Apache documentation only mentions an 8,192-byte limit on an individual field in a request.

Microsoft Internet Information Server (Server)
The default limit is 16,384 characters (yes, Microsoft's web server accepts longer URLs than Microsoft's web browser). This is configurable.

Perl HTTP::Daemon (Server)
Up to 8,000 bytes will work. Those constructing web application servers with Perl's HTTP::Daemon module will encounter a 16,384 byte limit on the combined size of all HTTP request headers. This does not include POST-method form data, file uploads, etc., but it does include the URL. In practice this resulted in a 413 error when a URL was significantly longer than 8,000 characters. This limitation can be easily removed. Look for all occurrences of 16x1024 in Daemon.pm and replace them with a larger value. Of course, this does increase your exposure to denial of service attacks

참고 URI :
https://stackoverflow.com/questions/812925/what-is-the-maximum-possible-length-of-a-query-string

 

What is the maximum possible length of a query string?

Is it browser dependent? Also, do different web stacks have different limits on how much data they can get from the request?

stackoverflow.com


POST 방식으로 바꾸면서 Controller에서 정적 메서드를 받아 처리하는 부분은 

아래 코드처럼 변경하고 심플하게 변경하였습니다.

@PostMapping("/generals/information")
public ResponseEntity<GenericCollectionResponse<Information>> getInformation(
	@RequestBody Search search){
	return ResponseEntity.ok(informationService
	.getinformation(search));
}

 

아래는 검색 파라미터가 많아지면서 정적 메서드를 만들어서 서비스에서 처리하는 구조로 변경하는 중입니다.


@Getter
@ToString
@NoArgsConstructor
public class Search {

    @Schema(description = "지역", example = "[\"1\", \"2\"]")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    protected List<String> areas = Collections.emptyList();

    @Schema(description = "업직종", example = "[\"1\", \"2\"]")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    protected List<String> parts = Collections.emptyList();

    @Schema(description = "기간", example = "[\"1\", \"0\"]")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected List<String> workPeriod = Collections.emptyList();

    @Schema(description = "요일", example = "[\"11\", \"10\"]")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected List<String> workdays = Collections.emptyList();;

    @Schema(description = "시간", example = "[\"10\", \"20\"]")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected List<String> workhour = Collections.emptyList();

    @Schema(description = "성별")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected GenderType gender;

    @Schema(description = "연령", example = "31")
    @JsonInclude(JsonInclude.Include.NON_DEFAULT)
    protected int age;

    @Schema(description = "학력", example = "[\"20\", \"10\"]")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected List<String> educationalStageType = Collections.emptyList();

    @Schema(description = "고용형태", example = "1")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected String employmentType;

    @Schema(description = "급여", example = "1222")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected String salary;
    
    .
    .
    .

	public static Search ofDefaultSearch(List<String> areas, List<String> parts, List<String> workPeriod,
                                  List<String> workdays, List<String> workhour) {
        return new Search(
                areas, parts, workPeriod, workdays, workhour
        );
    }

    public Search(List<String> areas, List<String> parts, List<String> workPeriod,
                  List<String> workdays, List<String> workhour,
                  GenderType gender, int age, String salary, String employmentType,
                  List<String> educationalStageType, List<String> welfareBenefits,
                  List<String> preferentialTerms, String acceptanceMethod,
                  String searchPeriod
    ){
        this.areas = areas;
        this.parts = parts;
        this.workPeriod = workPeriod;
        this.workdays = workdays;
        this.workhour = workhour;
        this.gender = gender;
        this.age = age;
        this.salary = salary;
        this.employmentType = employmentType;
        this.educationalStageType = educationalStageType;
        this.welfareBenefits =welfareBenefits;
        this.preferentialTerms = preferentialTerms;
        this.acceptanceMethod = acceptanceMethod;
        this.searchPeriod = searchPeriod;
    }
}

 


개발진 행하다 보면 조회를 구현할 때 Get으로 초반에 구현하면서 많은 파라미터 값을 처리해야 될 때 QueryString으로 처리하게 되면 코드레 벨이 길어질 수도 있고 Swagger Ui 표현해야 할 때 불필요한 코드가 들어가는 경우가 존재하는 것 같아 길어지는 느낌이라면 Post방식으로 변경 후 Json으로 처리하는 것도 방법인 것 같습니다

기능 개발하면서 Get , Post 방식을 사용하는 방법과 목적 

언제 Get을 쓰고 Post로 바꿔야 되는지 정리해봤는데...

각자 용도와 목적에 맞게 사용하면 좋을 것 같습니다.

오늘도 삽질한 내용을 기록하고 신규로 개발하면서 지속적으로 리펙토링 되어가고 고민해보는 부분들을 공유하도록 하겠습니다.

점점 바빠지다 보니까 -0- 블로그 쓰는 게 쉽지가 않네요

즐거운 하루 보내세요