[스프링부트핵심가이드] 05. API를 작성하는 다양한 방법

오늘내일·2023년 10월 29일
0

책 리뷰

목록 보기
4/11

5.2 GET API 만들기

controller패키지 내 컨트롤러 기능을 구현할 클래스를 만든다. 클래스에 @RestController를 붙인다.

클래스에 '@RequestMapping("/api")'를 붙이면 클래스 내 사용할 메서드에서 사용할 공통 URL로 '/api'를 사용할 수 있다.

5.2.1 @RequestMapping으로 구현하기
클래스 내 메서드에 '@RequestMapping("/hello", method = RequestMethod.GET)'을 붙이면 GET메소드를 통한 HTTP 요청을 처리할 수 있다. 클래스에 '@RequestMapping("/api")'를 붙이고 메서드에 '@RequestMapping("/hello", method = RequestMethod.GET)'을 붙인 경우 해당 요청 URL은 'http://localhost:8080/api/hello'가 된다.

@RequestMapping 대신 각 HTTP메소드를 특정하여 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping을 사용할 수 있다.이와 같은 어노테이션을 붙이는 것이 명시적으로 HTTP 메소드를 알 수 있기 때문에 이와 같은 방식이 주로 사용된다.

5.2.2 매개변수가 없는 GET 메서드 구현
@GetMapping을 사용할 경우 '@GetMapping(value = "/name")'과 같이 URL 주소를 명시하여 사용한다. 'value ='을 생략하고 괄호 안에 바로 URL 주소를 명시할 수 있다.

5.2.3 @PathVariable을 활용한 GET 메서드 구현

@GetMapping(value = "/member/{memberId}")
public String getMemberId(@PathVariable String memberId) {
	return memberId;
}

위와 같이 @PathVariable을 사용하면 URL에 memberId에 해당하는 값을 입력받아 메서드에서 변수를 사용할 수 있다. 위의 예시에서 URL 주소를 'http://localhost:8080/member/hello'와 같이 입력하여 요청하면, memberId변수의 값은 hello가 되므로 메서드가 호출되어 hello가 응답된다.

@GetMapping(value = "/member/{memberId}")
public String getMemberId(@PathVariable("memberId") String id) {
	return id;
}

URL 주소의 변수명과 메서드의 변수명이 다를 경우 @PathVaribale("memberId")와 같이 어노테이션의 괄호 안에 URL 주소의 변수명을 입력하여 사용할 수 있다.

5.2.4 @RequestParam을 활용한 GET 메서드 구현

@GetMapping("/member")
public String getMember(
		@RequestParam String name,
        @RequestParam int age) {
	return String.format("이름 : %s, 나이 : %d", name, age);
}

위와 같이 메서드를 구성해놓으면 쿼리 형식으로 값을 전달할 수 있다. 즉, 'http://localhost:8080/memeber?name=Robert&age=12'와 같이 요청을 하면 name변수에 Robert, age 변수에 12가 각각 할당되어, '이름 : Robert, 나이 : 12'가 RequestBody로 응답된다.

위 예제처럼 각 변수를 설정하지 않고 '@RequestParam Map<String, String> member'을 설정하면 쿼리로 들어오는 변수와 값을 map으로 받을 수 있다.

5.2.5 DTO 객체를 활용한 GET 메서드 구현
DTO(Data Transfer Object)는 각 클래스 및 인터페이스를 호출하면서 전달하는 매개변수로 사용되는 데이터 객체이다. 보통 dto패키지를 만들어 클래스를 구현하여 사용한다.

@Getter
@Setter
@ToString
public class Member {
	private String name;
    private int age;
}

위와 같이 매개변수로 사용할 클래스를 구현할 수 있다. 보통 lombok을 사용하여 getter/setter와 toString을 사용할 수 있게끔 구현한다.

@GetMapping("/member")
public String getMember(Member member) {
	return member.toString();
}

위와 같이 컨트롤러로 메서드를 구현하면 'http://localhost:8080/memeber?name=Robert&age=12'로 요청할 경우 Member클래스로 각 값들을 받을 수 있다.

5.3 POST API 만들기

GET API에서는 URL의 경로나 파라미터에 변수를 넣어 요청을 보냈지만, POST API에서는 저장하고자 하는 리소스나 값을 HTTP 바디에 담아 서버에 전달 한다.

5.3.2 @RequestBody를 활용한 POST 메서드 구현
일반적으로 POST 형식의 요청은 클라이언트가 서버에 리소스를 저장하는데 사용한다. 이때 HTTP Body에 값을 넣어 전송하는데 보통 JSON 형식으로 요청을 한다.

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

위와 같이 @RequestBody를 이용해서 json형식의 데이터를 Map으로 받을 수 있다. 또한 GET과 마찬가지로 아래와 같이 dto를 활용해서 데이터를 전달받을 수도 있다.

@PostMapping("/member")
public String postMember(@RequestBody Member member) {   
    return member.toString();
}

5.4 PUT API 만들기

PUT API는 웹 애플리케이션 서버를 통해 데이터베이스와 같은 저장소에 존재하는 리소스 값을 업데이트하는데 사용한다.

5.4.1 @RequestBody를 활용한 PUT 메서드 구현
PUT API를 사용하는 방법은 POST API의 @PostMapping을 @PutMapping으로 사용하면 된다.

@PutMapping("/member")
public String postMember(@RequestBody Member member) {   
    return member.toString();
}

컨트롤러에서 위와 같이 리턴값을 String형식으로 지정해서 응답하면, 지정한 문자열이 응답된다. 하지만 아래와 같이 dto객체를 응답하면 자동으로 ResponseBody에 json 형식으로 응답한다.

@PutMapping("/member")
public String postMember(@RequestBody Member member) {   
    return member;
}

5.4.2 ResponseEntity를 활용한 PUT 메서드 구현

@PutMapping("/member")
public String postMember(@RequestBody Member member) {   
    return ResponseEntity.status(HttpStatus.ACCEPTED)
    					.body(member);
}

위와 같이 리턴 값에 ResponseEntity를 사용하여 응답코드와 함께 값을 리턴할 수도 있다.

5.5 DELETE API 만들기

DELETE API는 웹 애플리케이션 서버를 거쳐 데이터베이스 등의 저장소에 있는 리소스를 삭제할 때 사용한다.

5.5.1 @PathVariable과 @RequestParam을 활용한 DELETE 메서드 구현
DELETE API는 GET API와 같이 URL주소에 값을 받거나 쿼리스트링으로 값을 받아 처리한다.

5.6 Swagger - REST API 문서화

api에 대한 설명을 작성하고, 요청 및 응답테스트를 위해 swagger를 많이 사용한다.

swagger를 사용하는 방법은 다음과 같다.

  1. 의존성 추가
    • gradle의 경우 build.gradle에 아래와 같이 의존성을 추가한다.
	implementation 'io.springfox:springfox-boot-starter:3.0.0'
	implementation 'io.springfox:springfox-swagger-ui:3.0.0'
    
  1. 설정에 관한 클래스 생성
    • 보통 config패키지를 생성하여 패키지 내에 아래와 같이 SwaggerConfiguration클래스를 생성한다.
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket aip(){
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("zerobase.weather"))
                .paths(PathSelectors.any())
                .build().apiInfo(apiInfo());
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("날씨 일기 프로젝트 :)")
                .description("날씨 일기를 CRUD 할 수 있는 백엔드 API입니다.")
                .version("2.0")
                .build();
    }
}
  1. 아래와 같이 기존 컨트롤러 코드의 메서드에 @ApiOperation을 붙이거나, 메서드 내 파라미터에 @ApiParam을 붙여 사용한다.

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

5.7 로깅 라이브러리 - Logback

로깅이란 애플리케이션이 동작하는 동안 시스템의 상태나 동작 정보를 시간순으로 기록하는 것을 의미한다. 로깅을 위해 logback라이브러리를 사용할 수 있다.(spring-boot-starter-web에 내장되어 있음)

로그레벨은 5개로 나뉜다.

  • ERROR : 로직 수행 중 심각한 문제가 발생해서 애플리케이션의 작동이 불가능한 경우
  • WARN : 시스템 에러의 원인이 될 수 있는 경고 레벨
  • INFO : 애플리케이션의 상태 변경과 같은 정보 전달을 위해 사용
  • DEBUR : 디버깅을 위한 메시지를 표시하는 레벨
  • TRACE : DEBUG레벨보다 더 상세한 메세지를 표현하기 위한 레벨

5.7.1 Logback 설정
resources폴더에 logback-spring.xml파일을 생성하여 아래와 같이 설정내용을 입력한다.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <!--변수값 설정-->
    <property name="LOGS_PATH" value="./logs"/>
    <property name="LOGS_LEVEL" value="INFO"/>

    <!-- Console Appender -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--출력 패턴 설정 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>

    <!-- File Appender-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 파일명과 경로 설정 -->
        <file>${LOGS_PATH}/log_file.log</file>
        <!--출력 패턴 설정 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz, .zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOGS_PATH}/%d{yyyy-MM-dd}_%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 폐기 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- Error Appender-->
    <appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 파일명과 경로 설정 -->
        <file>${LOGS_PATH}/error_file.log</file>
        <!--출력 패턴 설정 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz, .zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOGS_PATH}/%d{yyyy-MM-dd}_error.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- threshold filter 을 넣어서 eroor 이상의 로그만 걸러지도록 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>

    <root level="${LOGS_LEVEL}">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="Error"/>
    </root>
</configuration>

5.7.2 Logback 적용하기
로그를 작성하고 싶은 곳의 클래스에 객체 선언을 하고, 로그를 남기고 싶은 곳에 아래와 같이 코드를 입력한다.

// 객체선언(어플리케이션 전체에서 사용할 경우)
private static final Logger logger = LoggerFactory.getLogger(Application.class); 
// 로그 출력
logger.info("로그 기록이 출력됩니다.")

// 변수의 값을 로그로 출력도 가능(변수명이 variable인 경우 중괄호에 변수값 출력됨)
logger.info("{} 값을 출력", variable)
profile
다시 시작합니다.

0개의 댓글