[SpringBoot] 도서 관리 서비스 - API 생성하기
01_@SpringBootApplication
📍 @SpringBootApplication
@SpringBootApplication // 스프링을 실행시키기 위해 필요한 다양한 설정들을 모두 자동으로 해줌
public class LibraryAppApplication {
public static void main(String[] args) {
SpringApplication.run(LibraryAppApplication.class, args); // 스프링 애플리케이션을 실행(run)
}
}
02_네트워크와 API
📍 Network
데이터를 주고 받는 것(=택배)
📍 IP
각 컴퓨터의 고유 주소(=집 주소)
256을 넘지 않는 4개의 숫자로 이루어짐
ex. 244.66.51.9
📍 Domain Name
IP는 외우기 어려우니까 외우기 쉽게 별칭을 달아준 것
ex. 빨간집
📍 port number
컴퓨터에서 돌아가는 여러 프로그램 중 한 프로그램을 특정하는 것
ex. 3000
📍 HTTP
컴퓨터 간의 데이터를 주고 받는 표준(=운송장)
ex. 내놓아라 파란집 둘째, 빨간포션 2개
HTTP 요청
GET /portion?color=red&count=2
Host: spring.com:3000
HTTP method(GET) : HTTP 요청을 받는 컴퓨터에게 요구하는 행위(데이터를 달라)
path(/portion) : HTTP 요청을 받는 컴퓨터에게 원하는 자원
Query(?color=red&count=2) : 자원을 요구할 때 필요한 조건
Host : HTTP 요청을 받는 컴퓨터와 프로그램 정보
POST /oak/leather
Host: spring.com:3000
오크가죽정보
HTTP method(POST) : HTTP 요청을 받는 컴퓨터에게 요구하는 행위(데이터 저장해라)
path(/oak/leather) : HTTP 요청을 받는 컴퓨터에게 원하는 자원
Body(오크가죽정보) : 실제 저장할 정보
Host : HTTP 요청을 받는 컴퓨터와 프로그램 정보
- 그 외 HTTP Method
PUT : 데이터를 수정하라
DELETE : 데이터를 삭제하라
- 정보를 보내는 2가지 방법
- Query : GET, DELETE에서 사용
- body : POST, PUT에서 사용
HTTP 응답
HTTP/1.1 200 OK
Content-Type: application/json
{
"name":"A",
"age":null
}
- 상태코드
- 200 OK : 정상적으로 수행
- 300 Moved Permanently : 다른 곳으로 옮겨가라
- 404 Not Found : 요청을 찾을 수 없다
- 500 Internal Server Error : 내부에 문제가 생김
📍 API(Application Programming Interface)
정해진 약속을 하여 특정 기능을 수행하는 것
💡
첫째줄 - Method path Query
여러줄 - 헤더(여러 줄 가능)
(한 줄 띄기)
여러줄 - 바디(여러 줄 가능)
📍 URL(Uniform Resource Locator)
주소창
http://spring.com:3000/portion?color=red&count=2
http : 사용하고 있는 프로토콜
spring.com:3000 : 도메인 이름. 포트, 도메인 이름은 IP로 대체 가능
portion : path
color=red&count=2 : 쿼리. 추가 정보
03_GET API
Query를 사용하여 데이터 요청
📍 덧셈 API 명세서
HTTP Method : GET
HTTP Path : /add
Query(key, value) : int number1, int number2
API의 반환 결과 : int(두 숫자의 덧셈 결과)
📍 @RequestParam
controller/calculator/CalculatorController
package com.group.libraryapp.controller.calculator;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CalculatorController {
@GetMapping("/add") // GET /add
public int addTwoNumbers(@RequestParam int number1, @RequestParam int number2) {
return number1 + number2;
}
}
🔍 코드 설명
- @RestController : 주어진 클래스를 Controller(API의 진입 지점)으로 만들어 준다. API를 개발하려고 하는 클래스에 작성. 이 어노테이션이 있어야 클래스 안에 함수를 만들고 API 호출됐을 때 이 함수로 연결
- @GetMapping("/add") : GET /add. 아래 함수를 HTTP Method가 GET이고 HTTP path가 /add인 API로 지정한다.
- @RequestParam : Query를 통해서 넘어온 데이터를 함수 파라미터에 넣을 때 사용
@RequestParam 문제점 : Query가 늘어나면 함수의 파라미터도 길어짐
📍 DTO
해결책 : 객체를 받아서 사용
DTO(Data Transfer Object) : 정보를 전달하는 역할의 객체
dto/calculator/request/CalculatorAddRequest
package com.group.libraryapp.dto.calculator.request;
public class CalculatorAddRequest {
private final int number1;
private final int number2;
public CalculatorAddRequest(int number1, int number2) {
this.number1 = number1;
this.number2 = number2;
}
public int getNumber1() {
return number1;
}
public int getNumber2() {
return number2;
}
}
controller/calculator/CalculatorController
package com.group.libraryapp.controller.calculator;
import com.group.libraryapp.dto.calculator.request.CalculatorAddRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CalculatorController {
@GetMapping("/add")
public int addTwoNumbers(CalculatorAddRequest request) { // API 호출될 때 주어진 Query가 이 객체에 있는 값에 들어가게 됨
return request.getNumber1() + request.getNumber2();
}
}

04_POST API
HTTP body를 사용하여 데이터 받음
- JSON(JavaScript Object Notation)
객체 표기법
HTTP body에서 사용됨
{
"key": value
}
📍 곱셈 API 명세서
HTTP Method : POST
HTTP Path : /multiply
HTTP Body : { “number1”:숫자, “number2”:숫자 }
API의 반환 결과 : 숫자(곱셈 결과)
📍 코드로 구현
dto/calculator/request/CalculatorMultiplyRequest
package com.group.libraryapp.dto.calculator.request;
public class CalculatorMultiplyRequest {
private int number1;
private int number2;
public int getNumber1() {
return number1;
}
public int getNumber2() {
return number2;
}
}
controller/calculator/CalculatorController
package com.group.libraryapp.controller.calculator;
import com.group.libraryapp.dto.calculator.request.CalculatorAddRequest;
import com.group.libraryapp.dto.calculator.request.CalculatorMultiplyRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CalculatorController {
@PostMapping("/multiply") // POST /multiply
public int multiplyTwoNumbers(@RequestBody CalculatorMultiplyRequest request) {
return request.getNumber1() *request.getNumber2();
}
}
🔍 코드 설명
- @RequestBody : POST API에서 HTTP body 안에 담긴 json을 DTO 객체로 변환시킬 수 있음. DTO 객체 앞에 작성.

05_유저 생성 API
📍 도서관 사용자 등록 API 명세서
HTTP Method : POST
HTTP Path : /user
HTTP Body(JSON) : { “name”: String(null 불가능), “age”: Integer }
결과 반환 : x (HTTP 상태코드 200 OK면 충분)
📍 POST API 구현
dto/user/request/UserCreateRequest
package com.group.libraryapp.dto.user.request;
public class UserCreateRequest {
private String name;
private Integer age;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
domain/user/User
package com.group.libraryapp.domain.user;
// saveUser API가 사용돼서 유저가 저장되면 이 객체를 만들어서 실제 리스트에 저장
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
// API 통해서 들어온 name 값이 null이거나 비어있을 경우 예외 던짐
if (name == null || name.isBlank()) {
throw new IllegalArgumentException(String.format("잘못된 name이 들어왔습니다.", name));
}
this.name = name;
this.age = age;
}
}
controller/user/UserController
package com.group.libraryapp.controller.user;
import com.group.libraryapp.domain.user.User;
import com.group.libraryapp.dto.user.request.UserCreateRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class UserController {
// Instance 생성
private final List<User> users = new ArrayList<>();
@PostMapping("/user") // 1. POST /user가 실행되면
public void saveUser(@RequestBody UserCreateRequest request) { // 2. 함수 실행. json 형식으로 HTTP Body에 name과 age가 들어오면 DTO로 값이 매핑
users.add(new User(request.getName(), request.getAge())); // 3. 유저 생성. 만들어진 유저 객체는 Users 리스트에 저장된다.
} // 4. 200 OK 반환
}
06_유저 조회 API
📍 유저 조회 API 명세서
HTTP Method : GET
HTTP Path : /user
Query : 없음
결과 반환: [{ “id”: Long, “name”: String(null 불가능), “age”: Integer }, ...]
Controller에서 getter가 있는 객체를 반환하면 JSON이 된다.
- Id : 유저 별로 겹치지 않는 유일한 번호(Primary Key). 여기서는 저장할 때 List에 담겨 있는 유저의 순서를 id로 지정
package com.group.libraryapp.domain.user;
public class Fruit {
private String name;
private int price;
public Fruit(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
package com.group.libraryapp.controller.user;
import com.group.libraryapp.domain.user.Fruit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/fruit")
public Fruit fruit() {
return new Fruit("바나나", 2000);
}
}
JSON 형식으로 반환

📍 GET API 구현
domain/user/User
package com.group.libraryapp.domain.user;
// saveUser API가 사용돼서 유저가 저장되면 이 객체를 만들어서 실제 리스트에 저장
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
// API 통해서 들어온 name 값이 null이거나 비어있을 경우 예외 던짐
if (name == null || name.isBlank()) {
throw new IllegalArgumentException(String.format("잘못된 name이 들어왔습니다.", name));
}
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
dto/user/response/UserResponse
package com.group.libraryapp.dto.user.response;
import com.group.libraryapp.domain.user.User;
public class UserResponse {
private long id;
private String name;
private Integer age;
public UserResponse(long id, User user) {
this.id = id;
this.name = user.getName();
this.age = user.getAge();
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
controller/user/UserController
package com.group.libraryapp.controller.user;
import com.group.libraryapp.domain.user.Fruit;
import com.group.libraryapp.domain.user.User;
import com.group.libraryapp.dto.user.request.UserCreateRequest;
import com.group.libraryapp.dto.user.response.UserResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class UserController {
// Instance 생성
private final List<User> users = new ArrayList<>();
@PostMapping("/user") // 1. POST /user가 실행되면
public void saveUser(@RequestBody UserCreateRequest request) { // 2. 함수 실행. json 형식으로 HTTP Body에 name과 age가 들어오면 DTO로 값이 매핑
users.add(new User(request.getName(), request.getAge())); // 3. 유저 생성. 만들어진 유저 객체는 Users 리스트에 저장된다.
} // 4. 200 OK 반환
@GetMapping("/user")
public List<UserResponse> getUsers() { // UserResponse 반환
List<UserResponse> responses = new ArrayList<>(); // 빈 UserResponse 리스트 생성
for (int i = 0; i < users.size(); i++) {
responses.add(new UserResponse(i + 1, users.get(i))); // 우리가 갖고 있는 List<User> users를 userResponse로 변환
}
return responses;
}
}
- 현재 API의 문제점
서버 재시작하면 데이터 사라짐
유저 정보가 메모리에서만 유지되고 있기 때문