Closed wongakim-99 closed 1 month ago
이전 장에서 연습용 REST API 서버에 접속해 HTTP 요청을 보내고 응답받는 예제를 실습했다. 이때 사용하는 JSON 개념도 함께 살폈다. 클라이언트가 보내는 HTTP 요청 메시지의 첫 줄에는 시작 라인인 요청 라인(request line)이 있고, 그 아래에는 헤더(header)와 본문(body)가 있다.
결국 REST API란 REST 기반으로 API를 구현한 것이라고 할 수 있다. REST API를 잘 구현하면 클라이언트가 기기에 구애받지 않고 서버의 자원을 이용할 수 있을 뿐만 아니라, 서버가 클라이언트의 요청에 체계적으로 대응할 수 있어서 서버 프로그램의 재사용성과 확장성이 좋아진다.
REST API를 구현하려면 REST API의 주소, 즉 URL을 설계해야 한다. 게시판의 Article 데이터를 CRUD(생성, 조회, 수정, 삭제) 하기 위해 REST API 의 주소를 다음과 같이 설계하겠다.
GET : /articles GET : /articles/id POST : /articles PATCH : /articles/id DELETE : /articles/id
주소 설계가 끝났다면 URL 요청을 받아 그결과를 JSON으로 반환해 줄 컨트롤러도 만들어야 함. 게시판을 만들 때는 일반 컨트롤러(ArticleController)를 사용했지만, REST API로 요청과 응답을 주고받을 때는 REST 컨트롤러를 사용해야 한다. 그리고 응답할 때 적절한 상태 코드를 반환하기 위해 ResponseEntity라는 클래스도 활용.
저번주에 웹서버 프로그래밍 수업에서 jakarta EE로 Maven을 활용해서 어쩌구 저쩌구 하면서 프로젝트 생성을 진행하다 보니 뭔가 의존성 문제가 생겼다.
그러다 보니 간단하게 REST API 에서 FirstApiController.java를 추가하고 문제를 살펴보니...
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Failed to load driver class org.h2.Driver in either of HikariConfig class loader or Thread context classloader
이런 문제가 나왔다....대충 한글로 번역해보자니...
"클래스 경로 리소스 [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]에 정의된 이름이 'dataSource'인 빈을 생성하는 중 오류가 발생했습니다. [com.zaxxer.hikari.HikariDataSource]를 인스턴스화하는 데 실패했습니다. 팩토리 메서드 'dataSource'가 다음 메시지와 함께 예외를 발생시켰습니다. HikariConfig 클래스 로더 또는 Thread 컨텍스트 클래스 로더에서 드라이버 클래스 org.h2.Driver를 로드하는 데 실패했습니다."
대충 살펴보니 드라이버 클래스가 로드되지 않아서 발생한 문제같다. HikariCP가 H2 드라이버를 로드하려고 시도하는데, H2 Driver가 명시되지 않았거나, 올바르게 설정되지 않았던 문제였던 것이다.
하지만...스린이였던 내가 이걸 알리 없었다...폭풍 구글링, 폭풍 GPT 질문 후...나온 결론이 위의 내용으로 추정된다.
당연히 인생에서 날로 먹을 수 있는건 회만 있을 뿐...스프링 부트는 호락호락 하지 않았다. 당연히 실패
gradle 의존성 파일을 다시 새로고침하고 껐다 켜보기...당연히 두번째 날먹 실패
GPT에게 물어보니 pom.xml 파일에서
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.4</version>
</dependency>
</dependencies>
위와 같이 수정하면 된다고 했다. 그러나 눈씻고 찾아봐도 지금 현재 프로젝트에서 pom.xml을 찾을 수 없었다.
도대체 이 파일이 왜 안보이지...찾고 찾다가 C:\Users\kgw19.gradle\caches\modules-2\files-2.1\com.zaxxer\HikariCP\5.0.1\a74c7f0a37046846e88d54f7cb6ea6d565c65f9c\HikariCP-5.0.1.jar!\META-INF\maven\com.zaxxer\HikariCP\pom.xml 경로에 있다는거다. 아무리 봐도 느낌상 여기는 아닌것 같아서 GPT에게 물어봤다. 진짜로 여기 수정하는게 맞냐 물어봤지만 알고보니 위의 경로에 있는 pom.xml 파일은 Gradle의 캐시 디렉토리 안에 있는 파일이었다. 즉, Gradle이 의존성으로 다운로드한 라이브러리와 관련된 메타정보일 뿐, 내 프로젝트와 직접적으로 관련된 pom.xml 파일이 아니다. 수정할 필요도, 수정한다 해도 무의미했을 뿐이다.
머리를 계속 싸매다가 GPT 아래 답변이 킬포였다. 프로젝트 내에서 pom.xml 파일을 찾을 수 없는 경우 Gradle 기반 프로젝트일 가능성이 높다고 했다. Gradle 프로젝트는 pom.xml 대신 build.gradle 파일을 사용하여 의존성을 관리한다. 즉!!!! 이 파일을 찾아서 수정하면 되는 것이었다.
확인해보니 내 프로젝트는 Gradle 기반 프로젝트 였고, 여기에 H2 데이터베이스 의존성을 추가했다. 그러고 나서 다시 오류 목록을 보자하니... 드라이버 클래스 어쩌구 저쩌구를 로드하는데 실패, ",,,정의된 이름이 'dataSource'인 빈을 생성하는 중 오류가 발생했습니다."
그래서 application.properties 파일에서 spring.datasource.driver-class-name 설정을 해주었다.
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.platform=h2
그러더니 정상 작동ㅎ
Maven 과 Gradle 은 모두 빌드 자동화 도구로, 자바 프로젝트에서 의존성 관리, 빌드, 테스트, 배포 등을 쉽게 할 수 있게 도와준다. 하지만 이 둘은 구조와 철학에서 큰 차이가 있다.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.4</version>
</dependency>
</dependencies>
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:2.5.4'
}
postman을 켜서 메서드를 GET으로 선택하고 URL을 http://localhost:8080/api/hello 를 입력하고 send 버튼을 클릭
이처럼 REST 컨트롤러를 사용하면 REST API로 설계된 URL 요청을 받아 처리할 수 있다. 그렇다면 REST 컨트롤러와 일반 컨트롤러의 차이점은 무엇일까?
REST 컨트롤러(FirstApiController)는 JSON이나 텍스트 같은 데이터를 반환하는 반면 일반 컨트롤러(FirstController)는 뷰 페이지 반환
1. GET 요청 처리 (전체 게시글 조회)
그 다음에 http://localhost:8080/api/articles 로 GET요청 날리면 기존에 우리가 저장해놨던 더미 데이터 3개가 아래와 같이 잘 뜰거임
2. GET 요청 처리 (단일 게시글 조회)
Article article = dto.toEntity();
클라이언트에서 받은 수정 데이터가 담긴 dto를 DB에서 활용할 수 있도록 엔티티로 변환해 article 변수에 저장
Article target = articleRepository.findById(id).orElse(null);
DB에서 대상 엔티티를 조회해 가져옴. articleRepository.findById(id) 를 통해서 DB에서 해당 id를 가진 엔티티를 가져오되 없다면 null을 반환. 이렇게 반환한 값은 target이라는 이름의 변수에 저장
잘못된 요청이 들어온 경우를 처리해보자. 예를 들어, id 가 1,2,3 번까지 있는데 100번 데이터의 수정 요청이 들어왔다면 DB에 대상이 없을 것이다. 또 수정 요청은 1번으로 했는데, 요청 본문의 id가 3번이라면 요청 자체가 잘못된 것이다. 이런 경우를 처리하는 코드를 작성해보자.
@DeleteMapping 어노테이션을 쓰고 메서드 안에 크게 3부분으로 나누어 작성하겠다.
REST API REST 는 HTTP URL로 서버의 자원(resource)을 명시하고 HTTP 메서드(GET, POST, PATCH/PUT, DELETE)로 해당 자원에 대해 CRUD(생성, 조회, 수정, 삭제)하는 것을 말한다. 그리고 API는 클라이언트가 서버의 자원을 요청할 수 있도록 서버 측에서 제공하는 인터페이스(interface)이다. 결국 REST API란 REST 기반으로 API를 구현한 것이라고 할 수 있다.
REST 컨트롤러 REST API로 설계된 URL 요청을 받아 처리하는 컨트롤러이다. 일반 컨트롤러가 뷰 페이지를 반환한다면 REST컨트롤러는 JSON이나 텍스트같은 데이터를 반환한다.
REST 컨트롤러의 특징
ResponseEntity REST 컨트롤러의 반환형, 즉 REST API의 응답을 위해 사용하는 클래스이다.REST API 요청을 받아 응답할 때 이 클래스에 HTTP 상태 코드, 헤더, 본문을 실어 보낼 수 있다.
HttpStatus HTTP 상태 코드를 관리하는 클래스로, 다양한 Enum 타입과 관련한 메서드를 가진다. 상태 코드 200은 HttpStatus.OK, 201은 HttpStatus.CREATED, 400은 HttpStatus.BAD_REQUEST 등으로 관리됨
게시판 데이터를 CRUD(생성, 조회, 수정, 삭제)하기 위한 REST API를 구현해 보겠다.