[북스터디] 스프링 부트 핵심 가이드(ch4~ch5)를 공부해 보았다.(3편)ch4~ch5

Wang_Seok_Hyeon·2023년 3월 17일
0

주저리

요번 한 주는 ch4~ch5를 학습해 보았다. 실습 중심의 서적이라 그런지 ch3의 설정 부분이 ch4에서도 이어졌던 것 같다.

ch5도 실습 위주이긴 했지만 정보들이 있고, 그 분량이 꽤나 상당하고 유의미한 실습도 많아서 좋은 것 같다.

문제는 실제 서비스를 개발하고 있는게 아니기 때문에
해당 방식의 학습이 조금 붕 뜬 느낌을 받을 수 있다는 점인데,
이 경우에는 제로베이스 백엔드 스쿨 과정을 듣고 있는지라, 실무적인 감각을 잊지 않을 수 있어서 좋은 것 같다.
그리고 책을 통해 복습하고 정리하는 느낌까지 받고
어떤 부분이 주효하고, 정보인지를 빨리 캐치할 수 있었던 게 이번 정리에 요긴하게 작용했다.

ch4 간단 요약

이 부분은 한 번도 스프링부트를 만들어 보지 않은 분들을 위한 설정 가이드와 같은 부분이다.

해당 서적은 인텔리제이를 활용하고 Marven으로 파일을 설정하고 의존성을 추가하는 형태로 서적을 써 놓았다.

나는 수업에서 주로 하는 프로젝트를 Gradle로 만들기 때문에 이 부분에 대한 실습과는 조금 괴리가 있긴 하다.

애초에 Marven을 base로 Gradle이 나온 거여서 이 부분은 크게 신경을 쓰지 않았다.

(만약, Marven, Gradle, 의존성 이라는 용어가 생소하다면, 이 부분에 대해서 조금 이야기를 할 필요가 있다.
최초에 나도 이런 걸 접했을 때 너무 생소했기 때문에...

Marven과 Gradle은 쉽게 말하면 Web 파일로 만들어주는 도구다.
그리고 이러한 도구를 외부의 라이브러리를 추가해 기능을 더하는 걸 의존성을 추가한다.라는 표현을 사용한다.

이 정도만 정리하고 이해하고 나머지는 차차 사용하면서 이해할 수 있는 부분들이니 너무 심각하게 고민하지 않아도 된다. 초반에 그랬던 나...)

추가로 pom.xml에 의존성을 추가하는 형태들의 경우(Gradle의 경우 build.gradle 에서 할 수 있다.)

marven pom.xml 의존성 추가
gradle build.gradle 의존성 추가 라는 키워드로 검색해 보길 바란다. :)
의존성과 영어 (dependency)는 같은 용어이다.

이후 서적에서는 Talend API TESTER라는
크롬 확장 프로그램의 설치를 통해 API 호출 테스트를 한다.

필자의 경우 이미 강의 수강 중에 postman이라는 확장 앱을 다운 받아 사용하고 있기 때문에 따로 설정해 주지는 않았다.

이 처럼 호출 테스트 애플리케이션은 다양하게 있으므로 자신의 기호에 맞게 또는 처음 학습한 것에 맞춰서 따라가면 된다.

ch5를 분해해 보자!

ch5 부터가 핵심적인 요소들을 많이 가지고 있다.
크게 총 6가지를 설명하고 있다.

  • GET API 만들기
  • POST API 만들기
  • PUT API 만들기
  • DELETE API 만들기
  • +알파로 Swagger 사용법
  • +세타로 logback 사용법

위와 같은 구성으로 나뉜다. 앞선 4가지는 REST의 호출 4가지의 특징으로 구체적인 설명을 하기 전에 4가지의 활용에 관련한 간단한 정리를 하고 넘어가는 것이 좋다.

  1. GET 요청 = DATA를 불러오는 보여주기 형태의 READ 요청(View) 형태의 요청에 적절.

  2. POST 요청 = DATA를 저장하는 Client의 요청에 적절

  3. PUT 요청 = DATA를 수정하는 Client의 요청에 적절.

  4. DELETE 요청 = DATA를삭제하는 Client의 요청에 적절.

위의 특징이 데이터베이스에서 주로 언급되는 CRUD형태로 만들 수 있는 형태의 요약 정리에 해당한다.(C는 post, R은 get)

위와 같은 내용이 책 안에도 나오지만 각 파트별로 해서 분산되어서 나오는데, 필자의 경우 학습을 하며 위의 내용을 인지하고 위의 요청들을 떠올렸을 때 매칭이 잘 되고 이해도가 높아졌기 때문에 빠르게 소개했다.

이하는 자세한 설명들이다.
하나를 만들고 나면 그 항목들의 반복이기 때문에 반복되는 항목을 한 번에 정리하고 나머지를 소개하겠다.
(이게 AOP지 다른게 AOP가 아니라...책은 왜 AOP로 안 쓰셨..)

공통사항

우선 각각의 어노테이션을 이야기 해 보자.
Http에 보내는 REST 요청을 표시해 줄 어노테이션은 크게 2가지가 있지만 현재는 두 번째 소개해주는 형태로만 사용한다.(스프링 버전 4.3 이후)
실제 실습이나 여타 강의에서도 첫 번째를 사용하는 방식은 거의 없었으므로 역사적인 하나로만 이해하면 될 것 같다.

  • @RequestMapping 어노테이션
import .org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/api/v1/get-api")
public class GetController{
//http:/localhost:8080/api/v1/get-api/hello
@RequestMapping(value="/hello", method = RequestMethod.GET)
public String getHello() {
	return "Hello World";
}
}

위의 코드에서 보듯이
@RequestMapping(value="/hello", method = RequestMethod.GET)
REST요청을 명시적으로 메소드라는 형태로 주는 방식을 사용하며 코드가 상당히 길어지는 것을 볼 수 있다.
이와 같은 형태는 이후에 책에서 소개하지 않기 때문에 별도로 다루지 않는다.

이후 각각의 특징으로 소개할 전체의 4개 어노테이션은 아래와 같다.

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping

위의 어노테이션들을 통해 직관적으로 앞에서 확인할 수 있는 방식으로
형태가 변경되었다.

책에서 몇 가지 겹치는 요소인 공통요소를 블로그에서는 미리 정리하고자 한다.

이는 매개변수를 받는 것에 대한 내용이며, 책은 실습과 이를 통한 익숙도 증가를 위해 모두 기술하는 수고를 선택한 것 같지만 정리는 이를 요약하고 축약하는 형태로 진행하고자 한다.

실제로 우리가 사용하는 웹의 URL을 살펴보면 해당 URI에 들어있는 수많은 데이터에 실제로 다양한 값들이 들어가 있는 것을 볼 수 있다.

이러한 것들을 매개변수, Parameter라고 부르며 이러한 값들에 의해
우리가 보내는 요청의 결과를 수행하거나 검증하는 방식으로 웹은 동작한다.

그렇다면 프로그래밍에서는 이를 판단하는 방법을 어떻게 사용할까?

스프링에서는 크게 두 가지 어노테이션을 활용한다.

@PathVariable
@RequestParam/@RequestBody(PUT의 경우 주로 활용)

어노테이션의 앞단에 나와 있듯 한 가지는 경로를 설정하는 것이고
다른 하나는 요청을 보내는 방식의 어노테이션이다.

@PathVariable이라는 어노테이션의 경우 중괄호{ } 를 활용해 특정 값이 들어가야 한다는 것을 표시해주고, 나아가서는 format도 지정해 줄 수 있는 형태인데, 서적에서는 format까지는 지금 다루지 않고 있다.

@RequestParam의 경우
Json 형태의 키와 값이 들어가는 형태로 구현해, 어떤 값이든 들어올 수 있는 자율성을 보장해주는 형태를 구현할 수 있다.

이를 구현할 때는 Map을 활용해 구현하기도 하며 책의 실습에서도 이를 제공하고 있으며 여타 프로젝트를 진행할 때도 Map을 사용했던 경험이 있기에 꽤 주요했던 방식 중 하나인 것 같다.

그리고 웹에 공통으로 사용하는 URL에 관한 설정을
@RestController 어노테이션을 통해 표현하는데 해당 내용도 중복되는 내용이므로 먼저 먼저 소개한다.

즉, 앞으로 나오는 내용들은 위의 어노테이션이 생략되어 있어도 이해해 주기 바란다. :)
서적에서는 각각의 요청마다 해당 형태와 공통 URL을 아래의 어노테이션으로 만들어 주고.
각각의 클래스를 만든 형태를 보여주고 있다.
하지만 앞서 말했듯이 공통되는 요소로 한 번 소개하고 이를 생략한다ㅏ.
@RequsetMapping("/api/v1/get-api")

@RestController
@RequsetMapping("/api/v1/get-api")
public class GetController(){(실제 아래 코드가 들어가는 곳)}
public class PostController(){(실제 아래 코드가 들어가는 곳)}
public class PutController(){(실제 아래 코드가 들어가는 곳)}
public class DeleteController(){(실제 아래 코드가 들어가는 곳)}

Public class로 선언되어 있기 때문에 알 수 있지만 각각의 요청 별로 Controller를 나눠서 관리하고 있는 형태로 서적은 작성되었다.

자신들이 진행하는 프로젝트의 규모나 형태에 따라서 위의 상황은 달라질 수 있다는 점도 유의하자 :)

위의 공통사항을 정리했으므로 이제 부터는 아래에 코드를 통해 핵심 내용들을 알아 보자!

1.@GetMapping

첫 번째는 매개변수가 없을 때의 코드다. 이런 경우는 앞서 소개한 것처럼 드물다.

코드 맨 위의 주석의 경우 자신이 설정한 패키지의 경로의
맨 마지막 부분/name이라고 한 부분을 통해 이 코드의 형태를 유추할 수 있다.

//http://localhost:8080/api/v1/get-api/name
@GetMapping(value="/name")
public String getNamer(){
	return "Flature";
}

위와 같은 동작을 REST 요청 앱을 통해 실행해 보면 그 값을 직접 눈으로 확인해 볼 수 있다.

두 번째로는 매개변수를 넣는 경우다. 이 경우는 위에 설명한 것처럼 중괄호를 넣는다. URI의 중괄호는 편의상 넣은 것으로
들어간 값의 String만 나온다는 점에 유의하자!

//http://localhost:8080/qpi/v1/get-api/variable1/{String 값}
@GetMapping(value ="/variable1/{variable}")
public String getVariable1 (@PathVariable String variable){
	return variable;
}

서적에서는 위의 방식 외에도 변수명을 동일하게 하지 않고 매핑하는 방법에 대해서도 소개하고 있는데 이 경우는 직접 서적을 구매해 확인하거나 다른 방식을 확인해 보도록 하자 :)

위와 같은 코드의 경우 자신이 URI에 넣는 값에 따라 요청을 보내면 그 요청을 직접 보여주는 형태를 확인할 수 있다!

마지막으로는 @RequestParam을 활용해 매개변수를 넣는 방법을 살펴 보자. 이 경우에는 기본 형태와 Map을 활용한 방법 2가지를 다 소개한다.

아래의 Map을 활용한 형태의 코드 편의성이 더 좋은 것 같아서 위의 소개 보다는 아래의 Map이 더 주요한 것으로 보이기 때문이다.

먼저, Map을 활용하지 않은 방식이다!

//http://localhost:8080/api/v1/get-api/request1?name=value1?email=value2&organization=value3
@GetMapping(value = "/request1")
public String getRequestParm1(
@RequestParam String name, @RequestParam String email, @RequestParam String organization) {
	return name + " " + email + " " + organization;
}

다음은 Map을 활용해 RequestParam을 사용한 방식이다.

//http://localhost:8080/api/v1/get-api/request2?key1=value1&key2=value2
@GetMapping(value = "/request2")
public String getRequestParam2(@RequestParam Map<String, String> param){
	StringBuilder sb = new StringBuilder();
param.entrySet().forEach(map -> {
	sb.append(map.getKey() + " : " + map.getValue() + "\n");
 });
	return sb.toString();
}

코드 줄로 따지면 위가 더 좋아보이지만 어노테이션을 일일이 붙여야 한다는 점에서 나는 불편하다는 느낌을 받았던 것 같다.
Map을 활용하면 조금 더 스마트해보인다는 느낌이고
실제 구현 예시에서도 많이 보이므로 아래의 형태를 지향해야 하지 않을까 싶다.

다만, 현장은 어떤 일이 있을지 알 수 없으므로 모든 방식과 방법에 대해 알아 둘 필요가 있다.

앞서 Json에 대해 이야기를 조금 했는데 Json은 JavaScript Object Notation의 약자로 대괄호[ 중괄호{ '키' : '값' } ] 의 형태를 가지는 자료구조 형태로 오늘 날에는 많은 곳에서 이를 활용해 데이터를 저장하고 값을 가져오는 표준형처럼 쓰인다. 위의 키와 값들 역시 이러한 json 형식을 통해 URI에 요청을 보내는 형태가 표준으로 사용되며 개발에 활용되고 있다.

추가로 서적에서는 Dto를 통한 구현 역시 소개하고 있는데 이를 소개하지는 않을 예정이다. Getter/Setter의 개념을 서적에서는 Lombok을 활용해 소개하고 있지도 않아서 조금 더 많은 설명이 들어가기도 하고

이를 통한 표현 방식이 위의 표현을 학습했다면 충분히 쉽게 익힐 수 있는 형태라고 생각했기 때문이다.

2.@PostMapping

PostMapping은 값을 저장하기 위해 사용된다고 했듯이,
바로 매개변수를 넣은 형태를 소개하고 있다. 또한 바로 Map을 활용한 소스 코드를 제공하고 있다.

//http://localhost:8080/api/v1/post-api/member
@PostMapping(value ="/member")
public String postMember(@RequestBody Map<String, Object> postData) {
	StringBuilder sb = new StringBuilder();

	postData.entrySet().forEach(map -> {
		sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
	
	return sb.toString();
}

여기서 위의 공통사항에서 언급한 것처럼 POST의 경우, @RequestBody를 활용하는 것을 볼 수 있다.
해당 형태를 통해 텍스트와 같은 데이터를 저장하는 방식으로 활용하기도 하므로 POST를 사용할 때는 @RequestBody를 사용한다고 매칭해 두는 것이 유용할 것으로 보인다.

위에서 언급했듯이 Dto를 활용한 구현은 생략한다.

3.@PutMapping

PutMapping의 경우, 수정을 할 때 사용하는 방식이라고 소개했다. DELETE와 마찬가지로 값을 변화시키는 형태(POST는 추가하는 형태)로 분류할 수 있다.

Put과 Delete의 가장 주요한 특징 중 하나는 자신이 바꾸는 범위에 대한 지정과 특정이 필요하다는 것이다. 이러한 요소가 존재하지 않으면
자신이 원하지 않는 것을 수정하거나 삭제하는 등의 문제가 발생하거나 불편이 발생할 수 있다는 점을 먼저 생각해 보고 접근하면 좋다.

서적에서는 @RequestBody과 Map을 활용한 방식으로 이에 맞게 접근하는 코드를 제공한다.

//http://localhost:8080/api/v1/put-api/member
@PutMapping(value="/member")
public String postMember(@RequestBody Map<String, Object> putData){

StringBuilder sb = new StringBuilder();
putData.entrySet().forEach(map -> {
	sb.append(map.getKey() + " : " + map.getValue() + "\n");
} );

return sb.toString();
}

이외에도 추가적으로 ResponseEntity를 활용한 PUT 메서드의 구현을 소개한다.
다만 이 부분의 이해도가 낮아 정리를 크게 하지는 못했다.

아래는 서적의 내용을 간략하게 기술한 것이다.

스프링 프레임워크- HttpEntity라는 클래스가 존재.
HttpEntity는
헤더(Header)와 Body로 구성
HTTP 요청과 응답을 구성

RequestEntity
ResponseEntity
HttpEntity를 상속받아 구현한 클래스

ResponseEntity는 서버에 들어온 요청-응답 데이터 구성해 전달
ResponseEntity는 HttpEntity로부터
<HttpHeaders와 Body>를 가지고
<자체적으로 HttpStatus를 구현>

//http://localhost:8080/api/v1/put-api/member3
@PutMapping(value = "/member3")
public ResponseEntity<MemberDto> postMemberDto3(@RequestBodt MemberDto memberDto) {
return ResponseEntity.status(HttpStatus.ACCEPTED).body(memberDto);
}

status에 넣을 수 있는 값은 다양
위 예제에서 사용한 HttpStatus.ACCEPTED
응답 코드 202를 가짐.
즉, 이 메서드를 대상으로 요청 수행시 응답 코드가 202로 변경됨.

위와 같은 내용으로 구성되어 있으며, 이러한 부분을 통해 응답코드를 조정할 수 있는 형태의 구현도 가능하다!

4.@DeleteMapping

마지막으로 DeleteMapping을 살펴보자

첫 번째는 @Pathvariable을 활용한 DELETE 메서드를 구현한 예이다.

//http://localhost:8080/api/v1/delete-api/{String value}
@DeleteMapping(value = "/{variable}" )
public String DeleteVariable(@PathVariable String variable) {
return variable;
}

@DeleteMapping 어노테이션에 정의한 value의 이름과 메서드의매개변수 이름을 동일하게 설정해야 삭제할 값이 주입됩니다. 또는 @RequestParam 어노테이션을 통해 쿼리스트링 값도 받을 수 있습니다.(예제 5.20)

마지막으로! @Pathvariable을 활용한 DELETE 메서드를 구현하면 아래와 같다.

//http://localhost:8080/api/v1/delete-api/request1?email=value
@DeleteMapping(value = "/request1")
public String DeleteVariable(@RequestParam String email) {
return “e-mail :” + email;
}

Delete의 경우 왜? 맵을 사용하지 않았을까?를 고민해 보았을 때, 지정해야 하는 요소들이 여럿이고, 이러한 부분은 query에서 설정해 주는 요소로 JpaRepository를 활용한 예여서, 서적에서는 아직 다루지 않은 것으로 보인다. 즉, 삭제라는 특정 명시 요소를 삭제해야 하기 때문에
Map을 활용한 어떤 요소가 들어올지 알 수 없는 형태로는 구현하지 않은 것을 이해할 수 있다!

다음은 +요소들을 정리해 볼 차례인데! 이 요인들은 크게 정리하는 것이 아니라 개념만 소개하고 간단하게 끝내고자 한다.

+알파 Swagger와 logback 간단 정리

Swagger의 경우, REST API 명세(사용방법)을 웹에 문서화 해주는 OpenSource 프로젝트다.

애초에 Document화 시켜주는 프로젝트가 여럿 있기 때문에 이 역시 골라 사용하면 되는데, 나도 실습을 Swagger로 했던지라 조금 반갑기도 했다.

설정 파일의 경우 Swagger 설정이라고 검색하면 쉽게 알 수 있기 때문에 주요 설정을 제외한 어노테이션 몇 가지만 소개한다.

@APiOperation
@ApiParam
위의 어노테이션을 활용하며
@APiOperation의 경우 (value = "", notes="",...) 다양하게
해당 Swagger를 꾸미거나 수정 변경하는 내용들을 많이 담고 있다.
@ApiParam도 마찬가지로(value = "", required= true,...)
와 같이 값을 변경해주고 , 필수 욧고인지를 표시해주거나, 예제를 작성하는 형태 등 다양하게 활용할 수 있다!

Swagger의 강점 중 하나는 Try it out이라는 오른쪽 상단의 버튼인데
이를 통해 간단한 예시 형태까지 보여주기 때문에 이를 활용해
손 쉽게 프로젝트를 협업하는 동료와 효율적으로 소통할 수 있는 방식을 제공한다.

이번 3편의 마지막 logback이다.
로깅으로 총 5가지의 단계로 나뉘어지는데
ERROR
WARN
INFO
DEBUG
TRACE
위의 5가지로 내용을 나눌 수 있다.

먼저 logback을 사용하는 이유가 성능이 기존에 나왔던
log4j 보다 강력해서라는데 그건 맞지만, logback 이후에
log4j2라는 더 강력한 게 나왔기 때문에 강력하다라는 표현 보다는
편리해서라고 이야기 하는 편이 더 나을 것 같다.
logback의 경우 별도의 의존성을 추가할 필요없이 기본으로
내장되어 있다는 장점이 있다.

logback의 설정에 관한 내용에 대해 상세하게 다루고 있어서
이 부분은 좋았지만 한편으로는 꽤 복잡하게 되어 있기도 하기 때문에
따로 시간을 내어 학습 해볼 것을 추천한다.
(한편으로는 사용하면서 익숙해지는 방식을 추천)

설정파일의 영역을 나누는 부분에 대해서만 간단한게 소개한다.
Property영역

Appender 영역
Encoder 영역
Pattern 영역

Root 영역

위의 내용을 떨어뜨려 놓은 이유는,
A-E-P는 필요에 따라 여럿 추가되기도 하기 때문이다.

이러한 부분까지는 아직 소개하지 않고 있는데, Exception 처리를 하는 부분을 이야기하지 않아서 그런 것으로 보인다.

위와 같은 logback 설정 파일의 형태를 볼 때 마다 느끼지만, Html은 그냥 필수구나...그런 생각을 한다.

해당 내용을 단순하게 글로만 정리해서 무슨 의미인지 의아한 분이라면

logback 설명 블로그
위의 블로그 내용을 통해 그 내용의 형태를 한 번 참고하기 바란다.

이러한 복잡해 보이는 설정을 하는 이유는 역설적이게도 편리하기 때문인데,
실제로 문제를 자신의 관리하에 두는 방식과 Spring-boot에서 제공하는 형태의 log만으로는 충분하지 않을 수 있다.(영어기도 하고)

실무적인 관점으로 봤을 때 이러한 log들을 통해 효과적으로 자신들의 코드 동작과 문제를 빠르게 발견하고 처리할 수 있기 때문에
log 방식 역시 필수적으로 알아야 하는 부분 중 하나다!

profile
하루 하루 즐겁게

0개의 댓글