GET API: 웹 애플리케이션 서버에서 값을 가져올 때 사용하는 API
✅ 컨트롤 클래스에 @RestController와 @RequestMapping 설정
@RestController
@RequestMapping("/api/v1/get-api")
public class GetController {
}
✅ @RequestMapping으로 구현하기
// http://localhost:8080/api/v1/get-api/hello
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String getHello() {
return "Hello World";
}
✅ 매개변수가 없는 GET 메서드 구현
// http://localhost:8080/api/v1/get-api/name
@GetMapping(value = "/name")
public String getName() {
return "Flature";
}
✅ @PathVariable을 활용한 GET 메서드 구현
// http://localhost:8080/api/v1/get-api/variable1/{String 값}
@GetMapping(value = "/variable1/{variable}")
public String getVariable(@PathVariable String variable) {
return variable;
}
// http://localhost:8080/api/v1/get-api/variable2/{String 값}
@GetMapping(value = "/variable2/{variable}")
public String getVariable2(@PathVariable ("variable") String var) {
// = public String getVariable2(@PathVariable (value = "variable") String var) {
return var;
}
✅ @RequestParam을 활용한 GET 메서드 구현
// http://localhost:8080/api/v1/get-api/request1?name=value1&email=value2&organization=value3
@GetMapping(value = "/request1")
public String getRequestParam1(@RequestParam String name, @RequestParam String email, @RequestParam String organization) {
return name + " " + email + " " + organization;
}
// 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();
}
✅ DTO 객체를 활용한 GET 메서드 구현
public class MemberDto {
private String name;
private String email;
private String organization;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getOrganization() {
return organization;
}
public void setOrganization(String organization) {
this.organization = organization;
}
@Override
public String toString() {
return "MemberDto{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", organization='" + organization + '\'' +
'}';
}
}
// http://localhost:8080/api/v1/get-api/request3?name=value1&email=value2&organization=value3
@GetMapping(value = "/request3")
public String getRequestParam3(MemberDto memberDto) {
return memberDto.toString();
}
POST API: 웹 애플리케이션을 통해 데이터베이스 등의 저장소에 리소스를 저장할 때 사용되는 API
✅ @RequestMapping으로 구현하기
@RestController
@RequestMapping("/api/v1/post-api")
public class PostController {
@RequestMapping( value = "/domain", method = RequestMethod.POST)
public String postExample() {
return "Hello Post API";
}
}
✅ @RequestBody를 활용한 POST 메서드 구현
// 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();
}
✅ DTO 객체를 활용한 POST API 구현
// http://localhost:8080/api/v1/post-api/member2
@PostMapping(value = "/member2")
public String postMemberDto(@RequestBody MemberDto memberDto) {
return memberDto.toString();
}
PUT API: 웹 애플리케이션 서버를 통해 데이터베이스 같은 저장소에 존재하는 리소스 값을 업데이트하는 데 사용. POST API와 비교하면 요청을 받아 실제 데이터베이스에 반영하는 과정에 차이가 있지만 구현하는 방법은 HTTP Body를 활용하기 때문에 거의 동일.
✅ @RequestBody를 활용한 PUT 메서드 구현
@RestController
@RequestMapping("/api/v1/put-api")
public class PutController {
// < 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();
}
}
✅ DTO 객체를 활용한 PUT 메서드 구현
// http://localhost:8080/api/v1/put-api/member1
@PutMapping(value = "/member1")
public String postMemberDto1(@RequestBody MemberDto memberDto) {
return memberDto.toString();
}
// http://localhost:8080/api/v1/put-api/member2
@PutMapping(value = "/member2")
public MemberDto postMemberDto2(@RequestBody MemberDto memberDto) {
return memberDto;
}
✅ ResponseEntity를 활용한 PUT 메서드 구현
public class HttpEntity<T> {
private final HttpHeaders headers;
@Nullable
private final T body;
...
}
public class ResponseEntity<T> extends HttpEntity<T> {
private final Object status;
...
}
// http://localhost:8080/api/v1/put-api/member3
@PutMapping(value = "/member3")
public ResponseEntity<MemberDto> postMember3(@RequestBody MemberDto memberDto) {
return ResponseEntity
.status(HttpStatus.ACCEPTED)
.body(memberDto);
}
DELETE API: 웹 애플리케이션 서버를 거쳐 데이터베이스 등의 저장소에 있는 리소스를 삭제할 때 사용
✅ @PathVariable을 활용한 DELETE 메서드 구현
@RestController
@RequestMapping("/api/v1/delete-api")
public class DeleteController {
// < @PathVariable >
// http://localhost:8080/api/v1/delete-api/{String 값}
@DeleteMapping(value = "/{variable}")
public String DeleteVariable(@PathVariable String variable) {
return variable;
}
}
✅ @RequestParam을 활용한 DELETE 메서드 구현
// http://localhost:8080/api/v1/delete-api/request1?email=value
@DeleteMapping(value = "/request1")
public String getRequestParam1(@RequestParam String email) {
return "e-mail : " + email;
}
📌 pom.xml 파일에 Swagger 의존성 추가
<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
@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 명세를 추가(@RequestParam을 활용한 GET 메서드)
@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;
}
로깅(logging): 애플리케이션이 동작하는 동안 시스템의 상태나 동작 정보를 시간순으로 기록하는 것.
✅ Logback 설정
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- property 영역 -->
<property name="LOG_PATH" value="./logs"/>
<!-- Appenders 영역 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!-- encoder 영역 -->
<encoder>
<!-- pattern 영역 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<file>${LOG_PATH}/info.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/info_${type}.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- encoder 영역 -->
<encoder>
<!-- pattern 영역 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<!-- TRACE > DEBUG > INFO > WARN > ERROR > OFF -->
<!-- Root Logger -->
<!-- root 영역 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="INFO_LOG"/>
</root>
</configuration>
📌 Appender 영역
Appender 영역: 로그의 형태를 설정하고 어떤 방법으로 출력할지를 설정하는 곳. 하나의 인터페이스를 의미하며, 하위에 여러 구현체 존재
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
... 중략 ...
<appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
... 중략 ...
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
패턴 | 의미 |
%Logger{length} | 로거의 이름 |
%-5lebel | 로그 레벨. -5는 출력 고정폭의 값 |
%msg(%message) | 로그 메시지 |
%d | 로그 기록 시간 |
%p | 로깅 레벨 |
%F | 로깅이 발생한 애플리케이션 파일명 |
%M | 로깅이 발생한 메서드 이름 |
%I | 로깅이 발생한 호출지의 정보 |
%thread | 현재 스레드명 |
%t | 로깅이 발생한 스레드명 |
%c | 로깅이 발생한 카테고리 |
%C | 로깅이 발생한 클래스명 |
%m | 로그 메시지 |
%n | 줄바꿈 |
%r | 애플리케이션이 실행 후 로깅이 발생한 시점까지의 시간 |
%L | 로깅이 발생한 호출 지점의 라인 수 |
📌 Root 영역
Root 영역: 설정 파일에 정의된 Appender를 활용하려면 Root 영역에서 Appender를 참조해서 로깅 레벨을 설정
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="INFO_LOG"/>
</root>
또는
<logger name="com.springboot.api.controller" level="DEBUG" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="INFO_LOG"/>
</logger>
✅ Logback 적용하기
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATH" value="./logs"/>
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<!-- TRACE > DEBUG > INFO > WARN > ERROR > OFF -->
<!-- Root Logger -->
<root level="INFO">
<appender-ref ref="console"/>
</root>
</configuration>
Logback은 출력할 메시지를 Appender에게 전달할 Logger 객체를 각 클래스에서 정의해서 사용
private final Logger LOGGER = LoggerFactory.getLogger(GetController.class);
// http://localhost:8080/api/v1/get-api/hello
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String getHello() {
LOGGER.info("getHello 메소드가 호출되었습니다.");
return "Hello World";
}
// http://localhost:8080/api/v1/get-api/name
@GetMapping(value = "/name")
public String getName() {
LOGGER.info("getName 메소드가 호출되었습니다.");
return "Flature";
}
// http://localhost:8080/api/v1/get-api/variable1/{String 값}
@GetMapping(value = "/variable1/{variable}")
public String getVariable1(@PathVariable String variable) {
LOGGER.info("@PathVariable을 통해 들어온 값 : {}", variable);
return variable;
}
📌 다운로드
ORM: Object Relational Mapping. 객체 관계 매핑. 객체와 RDB(Relational Database)의 테이블을 자동으로 매핑하는 방법
클래스는 데이터베이스의 테이블과 매핑하기 위해 만들어진 것이 아니기 때문에 RDB 테이블과 어쩔 수 없는 불일치가 존재 -> ORM은 불일치와 제약사항을 해결하는 역할
JPA(Java Persistence API): 자바 진영의 ORM 기술 표준으로 채택된 인터페이스 모음. ORM이 큰 개념이라면 JPA는 더 구체화된 스펙을 포함. JPA 또한 실제로 동작하는 것이 아니고 어떻게 동작해야 하는지 메커니즘을 정리한 표준 명세
하이버네이트: 자바의 ORM 프레임워크. JPA가 정의하는 인터페이스를 구현하고 있는 JPA 구현체.
✅ Spring Data JPA
Spring Data JPA: JPA를 편리하게 사용할 수 있도록 지원하는 스프링 하위 프로젝트 중 하나. CRUD 처리에 필요한 인터페이스를 제공하며, 하이버네이트의 엔티티 매니저(EntityManager)를 직접 다루지 않고 리포지토리를 정의해 사용함으로써 스프링이 적합한 쿼리를 동적으로 생성하는 방식으로 데이터베이스를 조작. 이를 통해 하이버네이트에서 자주 사용 되는 기능을 더 쉽게 사용할 수 있게 구현한 라이브러리.
영속성 컨텍스트(Persistence Context): 애플리케이션과 데이터베이스 사이에서 엔티티와 레코드의 괴리를 해소하는 기능과 객체를 보관하는 기능을 수행.
✅ 엔티티 매니저
엔티티 매니저(EntityManager): 엔티티를 관리하는 객체. 엔티티 매니저는 데이터베이스에 접근해서 CRUD 작업을 수행. Spring Data JPA를 사용하면 리포지토리를 사용해서 데이터베이스 접근하는데, 실제 내부 구현체인 SimpleJpaRepository가 아래와 같이 리포지토리에서 엔티티 매니저를 사용하는 것을 알 수 있음
public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
this.escapeCharacter = EscapeCharacter.DEFAULT;
Assert.notNull(entityInformation, "JpaEntityInformation must not be null!");
Assert.notNull(entityManager, "EntityManager must not be null!");
this.entityInformation = entityInformation;
this.em = entityManager;
this.provider = PersistenceProvider.fromEntityManager(entityManager);
}
✅ 엔티티의 생명주기
📌 비영속(New): 영속성 컨텍스트에 추가되지 않은 엔티티 객체의 상태를 의미
📌 영속(Managed): 영속성 컨텍스트에 의해 엔티티 객체가 관리되는 상태
📌 준영속(Detached): 영속성 컨텍스트에 의해 관리되었던 엔티티 객체가 컨텍스트와 분리된 상태
📌 삭제(Removed): 데이터베이스에서 레코드를 삭제하기 위해 영속성 컨텍스트에 삭제 요청을 한 상태
1. @RequestBody와 Map 객체를 이용해 POST 요청을 보낼 때 다음 코드의 빈칸을 채우시오.
// http://localhost:8080/api/v1/post-api/member
// [1번 빈칸]
public String postMember(// [2번 빈칸] postData) {
StringBuilder sb = new StringBuilder();
postData.entrySet().forEach(map -> {
sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
return sb.toString();
}
2. @RequestParam을 활용한 GET 메서드인 다음 코드에 Swagger 명세를 추가하시오.
// http://localhost:8080/api/v1/get-api/request1?name=value1&email=value2&organization=value3
@GetMapping(value = "/request1")
public String getRequestParam1(
@RequestParam String name,
@RequestParam String email,
@RequestParam String organization) {
return name + " " + email + " " + organization;
}
1번 답
// http://localhost:8080/api/v1/post-api/member
@PostMapping(value = "/member") // 1번 빈칸 답
public String postMember(@RequestBody Map<String, Object> postData) { // 2번 빈칸 답
StringBuilder sb = new StringBuilder();
postData.entrySet().forEach(map -> {
sb.append(map.getKey() + " : " + map.getValue() + "\n");
});
return sb.toString();
}
2번 답
@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;
}
[스프링 1팀] 7장. 테스트 코드 작성하기 (0) | 2024.12.27 |
---|---|
[스프링 1팀] 6장 데이터베이스 연동 (0) | 2024.11.29 |
[스프링 1팀] 1장~4장. 스프링 부트 개발 환경과 애플리케이션 개발하기 (0) | 2024.11.15 |
[스프링 1팀] 스프링 입문 섹션 7~8 (0) | 2024.11.08 |
[스프링 1팀] 스프링 입문 섹션 5~6 (0) | 2024.10.11 |