북스터디 : 스프링 부트 핵심가이드(2)

윤장원·2023년 3월 18일
0

4. 스프링 부트 애플리케이션 개발하기

프로젝트 생성

스프링 공식 사이트에서 프로젝트 생성하기

스프링 공식 사이트에는 스프링 부트 프로젝트를 자동으로 만들어주는 서비스가 있다.

https://start.spring.io/

위 경로를 접속하면 다음과 같은 화면이 나온다.

이 페이지에서 왼쪽에 각 항목을 선택하면 프로젝트 설정을 할 수 있다. 그리고 오른쪽 [ADD DEPENDENCIES..] 버튼을 클릭하여 Dependencies 항목을 채울 수 있다.

pom.xml(Project Object Model) 살펴보기

pom.xml 파일은 메이븐의 기능을 사용하기 위해 작성하는 파일이다. 이 파일에는 프로젝트, 의존성 라이브러리, 빌드 등의 정보 및 해당 프로젝트를 관리하는 데 필요한 내용이 기술돼 있다.

메이븐

아파치 메이븐은 자바 기반의 프로젝트를 빌드하고 관리하는 데 사용되는 도구이다. 메이븐의 가장 큰 특징은 pom.xml 파일에 필요한 라이브러리를 추가하면 해당 라이브러리에 필요한 라이브러리까지 함께 내려받아 관리한다는 점이다. 메이븐의 대표 기능에는 프로젝트 관리, 빌드 및 패키징, 테스트, 배포가 있다.

메이븐의 기능은 생명주기 순서에 따라 관리되고 동작한다. 인텔리제이 IDEA에서 생성한 프로젝트의 경우 인텔리제이 IDEA에서 우측에 있는 'Maven' 탭을 클릭하면 메이븐의 생명주기를 확인할 수 있다. 메이븐의 생명주기는 크게 기본 생명주기(Default Lifecycle), 클린 생명주기(Clean Lifecycle), 사이트 생명주기(Site Lifecycle)의 3가지로 구분한다.

5. API를 작성하는 다양한 방법

1. Get API

별도의 매개변수 없이 GET API를 구현하는 경우 다음과 같이 코드를 작성할 수 있다.

@GetMapping(value = "/name")
public String getName() {
	return "Flature";
}

URL 자체에 값을 담아 전달되는 요청을 처리할 때 @PathVariable을 활용한다.

@GetMapping(value = "/variable1/{variable}")
public String getVariable1(@PathVariable String variable) {
	return variable;
}

@GetMapping 어노테이션에 지정한 변수의 이름과 메서드 매개변수의 이름을 동일하게 맞추기 어렵다면 @PathVariable 뒤에 괄호를 열어 @GetMapping 어노테이션의 변수명을 지정한다.

@GetMapping(value = "/variable2/{variable}")
public String getVariable2(@PathVariable("variable") String var) {
	return var;
}

URI에서 '?'를 기준으로 우측에 '{키}={값}' 형태로 구성된 요청을 전송할 때는 @RequestParam을 활용하면 된다.

@GetMapping(value = "/request1")
public String getRequestParam1(
	@RequestParam String name,
    @RequestParam String email,
    @RequestParam String organization
) {
	return name + " " + email + " " + organization;
}

만약 쿼리스트링에 어떤 값이 들어올지 모른다면 Map 객체를 활용할 수 있다.

@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");
    });
}

POST API

POST API는 웹 애플리케이션을 통해 데이터베이스 등의 저장소에 리소스를 저장할 때 사용되는 API이다. 저장하고자 하는 리소스나 값을 HTTP 바디(body)에 담아 서버에 전달한다. 바디 영역에 작성되는 값은 일정한 형태를 취한다. 일반적으로 JSON 형식으로 전송되며, 이렇게 서버에 들어온 요청은 다음과 같이 @RequestBody를 활용한다.

@PostMapping(value = "/member")
public String postMember(@RequestBody Map<String, Object> postData) {
	String Builder sb = new StringBuilder();
    
    postData.entrySet().forEach(map -> {
    	sb.append(map.getKey() + " : " + map.getValue() + "\n");
    });
    
    return sb.toString();
}

Map 객체는 요청을 통해 어떤 값이 들어오게 될지 특정하기 어려울 때 주로 사용한다. 요청 메시지에 들어갈 값이 정해져 있다면 DTO 객체를 매개변수로 삼아 작성할 수 있다.

PUT API

PUT API는 웹 애플리케이션 서버를 통해 데이터베이스 같은 저장소에 존재하는 리소스 값을 업데이트하는 데 사용한다. 컨트롤러 클래스를 구현하는 방법은 POST API와 거의 동일하다. 마찬가지로 HTTP body로 리소스를 서버에 전달한다.

@PutMapping(value = "/member")
public String postMember(@RequestBody Map<String, Object> putData) {
	String Builder sb = new StringBuilder();
    
    postData.entrySet().forEach(map -> {
    	sb.append(map.getKey() + " : " + map.getValue() + "\n");
    });
    
    return sb.toString();
}

DTO 객체를 활용한 PUT 메서드 구현은 다음과 같다.

@PutMapping(value = "/memeber2/")
public MemberDto postMemberDto2(@RequestBody MemberDto memberDto) {
	return memberDto;
}

@RestController 어노테이션이 지정된 클래스는 @ResponseBody를 생략할 수 있는데 이 @ResponseBody 어노테이션은 자동으로 값을 JSON과 같은 형식으로 병환해서 전달하는 역할을 수행한다.

스프링 프레임워크에는 HttpEntity라는 클래스가 있다. HttpEntity는 헤더와 바디로 구성된 HTTP 요청과 응답을 구성하는 역할을 수행한다. RequestEntity와 ResponseEntity는 HttpEntity를 상속받아 구현한 클래스이다. 그중 ResponseEntity는 HttpEntity로부터 HttpHeaders와 Body를 가지고 자체적으로 HttpStatus를 구현한다. 이 클래스를 활용하면 응답 코드 변경은 물론 Header와 Body를 더욱 쉽게 구성할 수 있다. 메서드의 리턴타입에 ResponseEntity를 적용한 예시는 다음과 같다.

@PutMapping(value = "/member3")
public ResponseEntity<MemberDto> postMemberDto3(
	@RequestBody MemberDto memberDto
) {
	return ResponseEntity
    		.status(HttpStatus.ACCEPTED)
            .body(memberDto);
}

DELETE API

DELETE API는 웹 애플리케이션 서버를 거쳐 데이터베이스 등의 저장소에 있는 리소스를 삭제할 때 사용한다. 컨트롤러를 통해 값을 받는 단계에서는 간단한 값을 받기 때문에 GET 메서드와 같이 URI에 값을 넣어 요청을 받는 형식으로 구현된다.
@PathVariable을 이용하면 다음과 같다.

@DeleteMapping(value = "/{variable}")
public String DeleteVariable(@PathVariable String variable) {
	return variable;
}

@RequestParam 어노테이션을 통해 쿼리스트링 값도 받을 수 있다.

@DeleteMapping(value = "/reuqest1")
public String DeleteVariable(@RequestParam String email) {
	return "email : " + email;
}

REST API 명세를 문서화하는 방법 - Swagger

API를 개발하면 명세를 관리해야 한다. 명세란 해당 API가 어떤 로직을 수행하는지 설명하고 이 로직을 수행하기 위해 어떤 값을 요청하며, 이에 따른 응답값으로는 무엇을 받을 수 있는지를 정리한 자료이다.

Swagger를 사용하기 위해 pom.xml 파일에 의존성을 추가해야 한다.

<dependencies>
	<dependency>
    	<groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    
    <dependency>
    	<groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
</dependencies>

그 다음, Swagger와 관련된 설정 코드를 작성한다. 이 클래스는 설정(Configuration)에 관한 클래스로 config라는 패키지를 생성한 후에 그 안에 생성하는 것이 좋다.

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
	
    @Bean
    public Docket api() {
    	return new Docket(DocumentationType.SWAGGER_2)
        	.apiInfo(apiInfo())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.springboot.api"))
            .paths(PathSelectors.any())
            .build();
    }
    
    private ApiInfo apiInfo() {
    	return new ApiInfoBuilder()
        	.title("Spring Boot Open Api Test with Swagger")
            .description("설명 부분")
            .version("1.0.0")
            .build();
    }
}

Swagger를 더 잘 활용하기 위해 Controller 코드에서 @ApiOperation, @ApiParam을 활용할 수 있다.

@ApiOperation(value = "GET 메서드 예제", notes = "@RequestParam을 활용한 GET Method")
@GetMapping(value = "/request1")
public String getRequestParam1(
	@ApiParam(value = "이름", required = true) @RequestParam String name,
    @ApiParam(value = "이메일", required = true) @RequestParam String email,
    @ApiParam(value = "회사", required = true) @RequestParam String organization
) {
	return name + " " + email + " " + organization;
}
  • @ApiOperation : 대상 API의 설명을 작성하기 위한 어노테이션
  • @ApiParam : 매개변수에 대한 설명 및 설정을 위한 어노테이션. 매개변수뿐 아니라 DTO 객체를 매개변수로 사용할 경우 DTO 클래스 내의 매개변수에도 정의할 수 있다.

애플리케이션을 실행한 후 웹 브라우저를 통해 http://localhost:8080/swagger-ui.html 로 속하면 Swagger 페이지가 출력된다.

로깅 라이브러리 - Logback

로깅(logging)이란 애플리케이션이 동작하는 동안 시스템의 상태나 동작 정보를 시간순으로 기록하는 것을 의미한다.

로깅은 개발 영역 중 '비기능 요구사항'에 속한다. 즉, 사용자나 고객에게 필요한 기능은 아니지만 디버깅하거나 개발 이후 발생한 문제를 해결할 때 원인을 분석하는 데 꼭 필요한 요소이다.

자바 진영에서 가장 많이 사용되는 로깅 프레임워크는 Logback이다. Logback이란 log4j 이후에 출시된 로깅 프레임워크로서 slf4j를 기반으로 구현됐으며, 과거에 사용되던 log4j에 비해 월등한 성능을 자랑한다.

Logback의 특징은 다음과 같다.

  • 크게 5개의 로그 레벨(TRACE, DEBUG, INFO, WARN, ERROR)을 설정할 수 있다.
  • 실제 운영 환경과 개발 환경에서 각각 다른 출력 레벨을 설정해서 로그를 확인할 수 있다.
  • Logback의 설정 파일을 일정 시간마다 스캔해서 애플리케이션을 재가동하지 않아도 설정을 변경할 수 있다.
  • 별도의 프로그램 지원 없이도 자체적으로 로그 파일을 압축할 수 있다.
  • 저장된 로그 파일에 대한 보관 기간 등을 설정해서 관리할 수 있다.
  1. Logback 설정
    일반적으로 클래스패스(classpath)에 있는 설정 파일을 자동으로 참조하므로 Logback 설정 파일은 리소서 폴더 안에 생성한다. 파일명의 경우 일반적인 자바 또는 스프링 프로젝트에서는 logback.xml이라는 이름으로 참조하지만 스트링 부트에서는 logback-spring.xml 파일을 참조한다.
    설정파일 내용에는 Property 영역, Appender 영역, Encoder 영역, Pattern 영역, Root 영역이 있다.
    Appender 영역은 로그의 형태를 설정하고 어떤 방법으로 출력할지를 설정한다. Appender의 대표적인 구현체에는 ConsoleAppender, FileAppender, RollingFileAppender, SMTPAppender, DBAppneder가 있다.
    appender 요소의 class 속성에 각 구현체를 정의하고 하단에 filter 요소로 각 Appender가 어떤 레벨로 로그를 기록하는지 지정한다. 다음으로 encoder 요소를 통해 로그의 표현 형식을 패턴(pattern)으로 정의한다.
    설정 파일에 정의된 Appender를 활용하려면 Root 영역에서 Appender를 참조해서 로깅 레벨을 설정한다.
  2. Logback 적용
    Logback은 출력할 메시지를 Appender에게 전달할 Logger 객체를 각 클래스에 정의해서 사용한다.
@RestController
@RequestMapping("/api/v1/get-api")
public class GetController {
	
    private final Logger logger = LoggerFactory.getLogger(GetController.class);
    
    @GetMapping(value = "/name")
    public String getName() {
    	logger.info("getName 메서드가 호출되었습니다.");
        return "Flature";
    }
}

0개의 댓글