Spring Boot (+ RESTful)

RESTful 웹 서비스 구축 - @RestController / MySQL / Swagger-ui

wy-family 2024. 12. 23. 15:27

지금까지는 H2 DB를 사용했었는데, 이제는 MySQL DB를 사용한다.

설치하고 오자. community -> MySQL 8 버전, workbench 가 포함된 걸로 설치하자.

 


@RestController

- Spring MVC는 @RestController 을 사용해서 RESTful 서비스 구축을 지원한다.

Spring MVC의 RESTful 서비스에는 일반적으로 다양한 HTTP 메서드 (GET, POST, PUT, DELETE 등)에 해당하고

특정 URI (Uniform Resource Identifiers, URI 와 URL 은 같은 개념으로 이해) 에 매핑되는 엔드포인트 생성이 포함된다.

 

@RestController - vs - @Controller

- 큰 차이는 없다, 클라이언트의 요청이 오면 처리를 해주는 것이 컨트롤러의 역할

- 일반 컨트롤러의 경우에는 뷰를 가지고 있는 컨트롤러고, 레스트 컨트롤러는 뷰가 없다. 그냥 바로 데이터, JSON 포맷으로 데이터를 보내는 걸 레스트 컨트롤러.

@Controller와 @RestController는 Spring MVC에서 사용하는 어노테이션으로, 주요 차이점은 데이터 반환 방식에 있습니다.


1. @Controller

  • 주로 HTML 페이지 또는 **뷰(View)**를 반환할 때 사용합니다.
  • 반환값은 **뷰 리졸버(ViewResolver)**를 통해 JSP, Thymeleaf 등 HTML 파일로 매핑됩니다.

사용 예시:

@Controller
public class MyController {
    @GetMapping("/hello")
    public String sayHello(Model model) {
        model.addAttribute("message", "Hello, World!");
        return "hello"; // "hello.html" 또는 "hello.jsp"로 매핑됨
    }
}
  • 반환값 String → 뷰 이름으로 해석됩니다.
  • Model 객체를 통해 데이터를 뷰에 전달합니다.

2. @RestController

  • @RestController는 @Controller + @ResponseBody를 합친 어노테이션입니다.
  • 주로 JSON 또는 XML과 같은 데이터를 반환할 때 사용합니다.
  • 반환값은 뷰가 아닌 HTTP Response Body에 그대로 전달됩니다.

사용 예시:

@RestController
public class MyRestController {
    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, World!";
    }
}
  • 반환값 "Hello, World!" → HTTP Response Body에 담겨 클라이언트로 전송됩니다.
  • JSON 형태의 데이터 반환이 많으므로 RESTful API 구현에 많이 사용됩니다.

주요 차이점 정리

특징 @Controller @RestController
기능 View(HTML/JSP) 반환 JSON/XML과 같은 데이터 반환
어노테이션 조합 단순히 @Controller @Controller + @ResponseBody
뷰 리졸버(ViewResolver) 적용됨 적용되지 않음
사용 예 웹 페이지 렌더링 RESTful API 데이터 전송

결론

  • @Controller: 주로 뷰 페이지 반환용 (HTML 파일)
  • @RestController: RESTful API 구현용, JSON 또는 XML 데이터 반환

만약 JSON 데이터를 반환해야 한다면 @RestController를 사용하는 것이 적합합니다.


@RestController와 RESTful API 기본 개념 정리

1. RESTful API URI의 설계 원칙

  • /api/: RESTful API와 일반 컨트롤러를 구분하기 위해 사용되는 접두어입니다.
  • /resource/: 데이터 엔티티 이름을 사용합니다. 예: books, users
  • /{id}: 특정 데이터 식별자로, 보통 **PK(Primary Key)**가 사용됩니다.
  • HTTP 메서드에 따라 동일한 URI라도 동작이 달라집니다.

2. RESTful API 메서드와 URI 매핑 예시

Method URI 설명
GET /api/books 전체 데이터 가져오기
GET /api/books/{id} 특정 데이터 가져오기
POST /api/books 데이터 저장하기
PUT /api/books/{id} 데이터 수정하기
DELETE /api/books/{id} 데이터 삭제하기

3. HTTP 메서드와 URI의 원리

GET (데이터 조회)

  • 서버에서 데이터를 조회할 때 사용됩니다.
  • @GetMapping 어노테이션으로 구현합니다.
@GetMapping("/api/books/{id}")
public Book getBookById(@PathVariable Long id) {
    // 특정 ID로 Book 조회
    return bookService.findById(id);
}

POST (데이터 저장)

  • 클라이언트가 데이터를 JSON 포맷으로 서버에 전달합니다.
  • 서버는 이 데이터를 객체로 변환해 처리합니다.
  • @RequestBody를 사용하면 JSON 데이터를 객체로 받을 수 있습니다.
@PostMapping("/api/books")
public Book saveBook(@RequestBody Book book) {
    return bookService.save(book);
}

PUT (데이터 수정)

  • 데이터를 수정하려면 JSON 포맷의 수정 데이터를 클라이언트가 전달합니다.
  • URI에 {id}를 포함하는 이유:
    • POST와 PUT은 구분이 필요합니다.
      • POST는 데이터 저장
      • PUT은 데이터 수정
    • {id}를 포함하지 않으면 POST와 PUT의 URI가 같아 혼란이 생길 수 있습니다.
  • JSON 데이터에 id를 포함할 필요가 없어진다는 장점이 있습니다.
@PutMapping("/api/books/{id}")
public Book updateBook(@PathVariable Long id, @RequestBody Book book) {
    return bookService.update(id, book);
}

DELETE (데이터 삭제)

  • 특정 데이터를 삭제할 때 사용됩니다.
  • URI에 **/{id}**로 식별자를 포함해 삭제할 데이터를 명확히 지정합니다.
@DeleteMapping("/api/books/{id}")
public void deleteBook(@PathVariable Long id) {
    bookService.delete(id);
}

 


4. Book 엔티티 클래스 예시

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Book { // DTO, VO 역할
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String subject;
    private int price;
    private String author;
    private int page;
    private LocalDateTime createdAt; // 등록 시간
}

5. 핵심 정리

  1. **/api/**는 RESTful 컨트롤러의 URI 접두어로 사용됩니다.
  2. HTTP 메서드에 따라 동일한 URI라도 동작이 달라집니다.
  3. PUT 요청은 수정할 대상을 명확히 하기 위해 {id}를 포함합니다.
  4. POSTPUT의 혼동을 방지하려면 URI를 일관되게 설계해야 합니다.
  5. **@RequestBody**를 사용하면 JSON 데이터를 객체로 변환할 수 있습니다.
- http://localhost:8081 이 부분은 서버의 주소이므로 별로 중요하지 않다.
- /api/ 라고 해주는것이 일반적이라고 했다.
- /books/ 라고 했는데, 어떤 메서드와 결합을 하든 다 똑같은 URI 인 이유는, 어차피 어떤 메서드와 결합되느냐에 따라 다른 결과가 되기 때문에 구분짓는건 상관이 없다. 그러므로, URI 는 일관성을 위해서 통일해주는 것.
-  {id} 의 경우에는 PK 가 오는 것이 일반적이다.
- GET은 이해가 잘 될 것. @GetMapping(~~~) 을 하면 되는 것.
- POST도 마찬가지. 저장을 하려면, 데이터를 넘길 때, 어떤 데이터 포맷으로 넘기냐면 클라이언트가 JSON 데이터 포맷으로 넘겨주면 되겠다. 그러면 서버는 JSON 데이터를 받아가지고 처리하면 된다. 그 때 서버에서 받는 어노테이션이 @RequestBody 이다. 이걸 이용하게 되면, JSON 데이터를 받아가지고 Object로 바꿀 수 있다. Object에다가 담아넣을 수 있다.  여기서 말하는 Object는, public class Book {} 했던, Book 이 될 것이다.
- PUT 은 수정하기이다. 그런데, 수정하려면 저장하기 처럼 수정할 데이터를 넘겨야 한다. 수정할 데이터도 마찬가지로 JSON 데이터로 넘기면 된다. 그러면 굳이 http://localhost:8081/api/books/{id} 에서, {id} 를 넘길 필요가 없이, http://localhost:8081/api/books 라고만 해도 되는 것이 아닌가? 왜냐면, 수정 데이터 안에 id 값이 있을 것이기 때문이다. 예를 들어, 어떤 Book 객체, (id 가 3 인 Book 객체) 의 subject, price, author 의 값을 수정한다고 해보자. 그러면 그 수정 데이터 안에는 id, subject, price, author 가 들어가 있다. id 가 들어가 있는 것. 그러면 그 id 값을 보고, 어떤 객체의 수정 데이터인지를 파악할 수 있을 것인데, 그럼에도 http://localhost:8081/api/books/{id} 해서 {id} 를 붙이는 이유가 있다.
PUT 에서 {id}를 뺀다고 해보자. 그러면 http://localhost:8081/api/books 가 된다. POST의 데이터 저장하기와 GET의 전체 데이터 가져오기의 http://localhost:8081/api/books 와 같아진다. 그렇지만, Method로 구분을 지을 수 있다고 했지 않았는가. 맞다. GET 하고는 구분이 되지만, POST와 PUT 은 구분이 안 된다. 수정하기와 저장하기는 DB가 변경이 되는건데, 수정과 저장하기는 구분이 안 된다. PUT이 일종의 POST랑 비슷한 요청이라고 보면 된다. 그래서 둘을 구분지어주기 위해서, PUT 의 경우에는 {id} 를 붙여서, PK 의 값을 붙여서 구분지어주는 것. 그렇게 되면, id 가 3 이라고 하면, 수정할 때의 JSON 이 넘어올때는 굳이 id 의 값을 포함할 필요는 없어지는 것.
- DELETE 는 삭제하기

Rest Project 를 시작해보자. Spring Initializr 로 가보자.

 

H2 가 아니라 MySQL

그리고 dependencies 에 springdoc 을 추가할 것이다.

Rest 프로젝트를 만들 때에는, REST API의 명세서가 필요하다. (명세서라는 표현이 맞는건가..?)

그 명세서를 word 라던가 excel 로 만들기에는 번거로운 점이 있어서,

spring boot 안에서 제공해주는 스웨거라는, REST API를, 서비스를 다큐멘테이션을 해줄 수 있는, 문서를 만들 수 있는 방법이 있는데, 그게 스웨거다.

스웨거 API 를 사용하려면, 아래의 것을 추가해주면 된다.

dependencies {
  implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
}

스웨거를 깔게 되면, 우리가  어차피 @RestController 로 open api 를 만드는 것인데 (REST API 자체가 그런 것),  그러면 스웨거가 자동으로 그 rest api를 인식을 해서, 우리가 만들 rest api url 을 만들어주고 테스트도 해볼 수가 있다.

 

가보자.

폴더 위치 잡아주고 나서, 오픈을 하자.

그리고나서 자바 버전 확인도 해주고,

그 다음에 dependencies 에 springdoc 을 추가해주자. 추가해주고 나서 refresh 버튼이 나올텐데 눌러주자.

그 다음에 properties 파일을 yml 로 바꿔주고 설정을 해보자.

MySQL DB 연동하는 것 / MySQL은 3306 포트로 열리게 되어 있다. 3306/books 라고 하면, books 는 DB의 이름인데, MySQL에 DB가 없으면 books 라는 DB를 하나 만들어놓으면 연결이 된다.

JPA 설정에서는 Dialect 만 바꿔주고 나머지는 다 동일하다. naming 전략으로, Entity의 멤버 변수의 이름과 DB의 열 이름과 똑같이 하기 위해 해당 전략을 집어넣어놓았다.

ddl-auto 는 처음에는 table 을 만들어야 하니까 create.

(DB의 이름을 books 로 하면 헷갈릴 수 있어서, sboot 으로 하기로함)

 

이제 Book 엔티티를 만들어주자. 위에서 예시로 만들었던 걸 그대로 가져가서 쓰자.

이제 컨트롤러를 만들어주자. BookRestController

- 패키지 이름은, controller 라고 해도 되고, restcontroller 라고 해도 상관없다.

- 어노테이션을 붙여줄 건데, @Controller 가 아니라, @RestController 를 붙인다.

- @RestController 는 @RequestMapping("/api") 을 함께 달아준다. 클라이언트의 어떤 요청(Request)을 받을 거냐면, /api 라는 모든 경로는 받기로 했다. /api/~ api 아래의 경로는 각각 만들어주면 되겠다.

- 일단은 연습 삼아 학습해보는거니까, @GetMapping("/test") 을 만들어보자. 만약에 클라이언트가 GET 방식으로, /api/test 라고 요청이 오면은, ( http://localhost:8081/api/test ), 그 요청을 처리할 컨트롤러가 필요한데 그 컨트롤러를 @RestController 로 만들고 있다. /api 는 @RequestMapping("/api") 로 받는 것이다. /api 로 요청이 오면은 BookRestController 가 요청을 처리한다고 보면 된다. 그 다음에, 그 아래쪽에 있는 세부 경로는 mapping 을 걸어주면 된다. 지금은 테스트를 해볼 것인데, GET 방식이기 때문에 GetMapping 이라 하고 괄호 안에 mapping URL 이름, mapping 주소 ("/test") 을 맞춰서 적어주면 된다. @GetMapping("/test")

- 그러면 결국은, 클라이언트가 GET: http://localhost:8081/api/test 이렇게 요청을 하게 되면은, test() 가 처리된다는 말이다. return 으로 "test is ok" 라는 String 을 그대로 응답을 해주게 된다.

- html 뷰 페이지로 간다던가 하는게 아니다. @RestController 는 바로 응답을 해주는 것과 같은 원리로 처리가 된다.

- 원래는 응답을 JSON 타입으로 할 건데, 지금은 테스트니까 String 타입으로 그냥 바로 응답을 해주는 것.

- 여기서 REST API 는 -->  GET: http://localhost:8081/api/test

 

- MySQL workbench를 열어서, DB를 만들어주자. books 말고 sboot 라는 DB로 만들어주자.

- MySQL 은 DBMS 이다. DBMS에다가 DB를 만들 수가 있다.

- create database sboot; 를 전체 드래그해서 번개표시(실행버튼)를 눌러주자.

 

 

그리고 나서, sboot 라는 DB 를 디폴트 데이터베이스로 설정을 해주어야 한다.

그러고나면 진한 볼드체가 된다.

이제 sboot 라는 데이터베이스에 접속이 될 것이고, 그 안에, Book 이라는 엔티티를 만들어놨으니 table 이 생성이 될 것이다.

그럼 root application에서 run 을 해보자.

table 이 잘 만들어진걸 확인할 수 있다.

 

어쨌든, 이제 연결이 다 된 것이기 때문에, 클라이언트가 되어서 브라우저에 요청을 해보자.

자, 이제 REST API 를 만들어낸것이다.

스웨거라는 spring document open api 를 dependencies 에 집어넣어놓았었다. 스웨거에 접속하려면 아래와 같다.

/swagger-ui/index.html

접속했고, BookRestController 와, @RequestMapping("/api"), @GetMapping("/test") 가 기록 되어 있는 걸 확인할 수 있다.

Try it out 을 클릭하면 execute 가 뜨는데, 버튼을 눌러보자. 브라우저에서 우리 서버로 GET /api/test 를 날려본다는 것.

http://localhost:8081/api/test 로 요청이 날라가니까,

200 의 의미는 성공을 의미, Response body 에 test is ok 가 날라갔다는 걸 보여주고 있다.

 

스웨거 ui 를 이용하면, 테스트까지 해볼 수 있다는 점. 브라우저에서 open api 인 REST API 를 관리하는 것.

이런 식으로도 관리가 가능하다.