๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Today I Learned(TIL)/์ˆ˜์ค€๋ณ„ ํ•™์Šต๋ฐ˜

์ˆ˜์ค€๋ณ„ ํ•™์Šต๋ฐ˜_๋ฒ ์ด์ง๋ฐ˜ 2ํšŒ์ฐจ ์„ธ์…˜

by carrot0911 2024. 12. 11.

๋ฒ ์ด์ง๋ฐ˜

API์™€ ์›น ์„œ๋ฒ„(MVC)

12/10 20:00 ~ 21:20 (์•ฝ 1์‹œ๊ฐ„ 20๋ถ„ ์ง„ํ–‰)

 

API (Application Programming Interface)

ํ”„๋กœ๊ทธ๋žจ๊ณผ ๋Œ€ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ• : ํด๋ผ์ด์–ธํŠธ(์‚ฌ์šฉ์ž ๋˜๋Š” ์™ธ๋ถ€ ์‹œ์Šคํ…œ)์ด ์„œ๋ฒ„์— ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ ๋‹จ์œ„์ด๋‹ค.

ํ•  ์ผ๊ณผ ๊ด€๋ จ๋œ 4๊ฐ€์ง€ API ์˜ˆ์‹œ

๋”๋ณด๊ธฐ
    /**
     * ํ• ์ผ ์ƒ์„ฑ API
     */
    @PostMapping
    public String createTodoAPI() {
        return "created";
    }

    /**
     * ํ• ์ผ ์กฐํšŒ API
     */
    @GetMapping
    public String getTodoAPI() {
        return "retrieve todo";
    }

    /**
     * ํ• ์ผ ์ˆ˜์ • API
     */
    @PatchMapping
    public String updateTodoAPI() {
        return "update todo";
    }

    /**
     * ํ• ์ผ ์‚ญ์ œ API
     */
    @DeleteMapping
    public String deleteTodoAPI() {
        return "delete Todo";
    }

 

RESTful API (Representational State Transfer API) - ๋ฉด์ ‘์—์„œ ๋ฌผ์–ด๋ณด๋Š” ๋‚ด์šฉ โญโญโญ

์ „ํ†ต์ ์ธ API ๋ฐฉ์‹

๋™์ž‘ ์ค‘์‹ฌ : ์ „ํ†ต์ ์ธ API ์„ค๊ณ„ ๋ฐฉ์‹์€ ๋™์ž‘ ์ค‘์‹ฌ ์„ค๊ณ„๋กœ ์ธํ•ด ํ™•์ž‘์„ฑ์ด ๋ถ€์กฑํ•˜๋‹ค.

API ์ข…๋ฅ˜ /createTodo /updateTodoTilte /markTodoDone /archiveTodo
API ๋™์ž‘ ํ• ์ผ ์ƒ์„ฑ ํ• ์ผ ์ˆ˜์ •(์ œ๋ชฉ) ํ• ์ผ ์ˆ˜์ •(์ƒํƒœ) ํ• ์ผ ์ˆ˜์ •(์ƒํƒœ)
  • ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ์ข‹์€ ์ 
    • ์„ค๊ณ„ํ•  ๋•Œ ๊ต‰์žฅํžˆ ๋ณต์žกํ•ด์ง„๋‹ค.....
    • ๋งŒ๋“œ๋Š” ์‚ฌ๋žŒ, ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ ๋ชจ๋‘ ๊ต‰์žฅํžˆ ํ˜ผ๋ž€์Šค๋Ÿฝ๋‹ค...

RESTful API ๋ฐฉ์‹ (rest architecture style)

์ž์› ์ค‘์‹ฌ : ์ž์› ์ค‘์‹ฌ์˜ API ์„ค๊ณ„, API์˜ ๋™์ž‘์€ HTTP METHOD๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค.

1. URL - ์ž์›์„ ๋ช…์‹œํ•œ๋‹ค.

URL /workspaces/1/todos
์˜๋ฏธ ์›Œํฌ์ŠคํŽ˜์ด์Šค 1์— ์†ํ•œ ํ• ์ผ
  • ๋ณดํ†ต ๋ณต์ˆ˜ํ˜•์„ ๊ถŒ๊ณ ํ•œ๋‹ค(์„ ํ˜ธํ•œ๋‹ค).

2. HTTP METHOD - ๋™์ž‘์„ ๋ช…์‹œํ•œ๋‹ค.

HTTP METHOD ์ข…๋ฅ˜ GET POST PATCH DELETE
์˜๋ฏธ ์กฐํšŒ ์ƒ์„ฑ ์ˆ˜์ • ์‚ญ์ œ

3. RESTful API๋Š” URL + HTTP METHOD ์กฐํ•ฉํ•ด์„œ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค.

  • (GET)  workspaces/1/todos
    • ์›Œํฌ์ŠคํŽ˜์ด์Šค 1์— ๋Œ€ํ•œ todo ๋ชฉ๋ก ์กฐํšŒ
  • (GET)  workspaces/1/todos/1
    • ์›Œํฌ ์ŠคํŽ˜์ด์Šค 1์— ๋Œ€ํ•œ todo ๋ชฉ๋ก ์ค‘, id๊ฐ€ 1์ธ todo ์กฐํšŒ
    • ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์‹๋ณ„์ž(id)๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
  • (POST)  workspaces/1/todos
    • ์›Œํฌ์ŠคํŽ˜์ด์Šค 1์— ๋Œ€ํ•œ todo ์ƒ์„ฑ
  • (PATCH)  workspaces/1/todos/1
    • ์›Œํฌ์ŠคํŽ˜์ด์Šค 1์— ๋Œ€ํ•œ todo ๋ชฉ๋ก ์ค‘, id๊ฐ€ 1์ธ ํ• ์ผ ์ˆ˜์ •
    • ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์‹๋ณ„์ž(id)๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
  • (DELETE)  workspaces/1/todos/1
    • ์›Œํฌ์ŠคํŽ˜์ด์Šค 1์— ๋Œ€ํ•œ todo ๋ชฉ๋ก ์ค‘, id๊ฐ€ 1์ธ ํ• ์ผ ์‚ญ์ œ
    • ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์‹๋ณ„์ž(id)๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

 

ํ˜„์—…์—์„œ ์‚ฌ์šฉ๋˜๋Š” API ๋ฌธ์„œ ์Šคํƒ€์ผ ์˜ˆ์‹œ

API ๋ฌธ์„œ ๋ชฉ๋ก

์•ฑ ์ด๋ฆ„ ์š”์ฒญ ๋ฉ”์„œ๋“œ URL API ์„ค๋ช… ์šฐ์„  ์ˆœ์œ„ ๋‹ด๋‹น์ž
todo GET /workspaces/1/todos ํ• ์ผ ์กฐํšŒ API ๊ธด๊ธ‰ ์˜๋ฌธ์˜ ๊ฐœ๋ฐœ์ž
todo POST /workspaces/1/todos ํ• ์ผ ์ƒ์„ฑ API ๋ณดํ†ต ์˜๋ฌธ์˜ ๊ฐœ๋ฐœ์ž

์˜ˆ) API ๋ฌธ์„œ ์ƒ์„ธ : ํ• ์ผ ์กฐํšŒ API

Request

๋”๋ณด๊ธฐ
  • URL
GET https://xxxx.com/workspaces/1/todos
  • Headers
ํ‚ค ๊ฐ’ ์„ค๋ช…
Authorization Bearer <ํ† ํฐ> ์‚ฌ์šฉ์ž ์ธ์ฆ ํ† ํฐ
Content-Type application/json ์š”์ฒญ ๋ณธ๋ฌธ ํ˜•์‹ ์ง€์ •(JSON
  • Query Parameters
ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„ ํ•„์ˆ˜ ์—ฌ๋ถ€ ํƒ€์ž… ์„ค๋ช…
author ์„ ํƒ String ํ• ์ผ์„ ์ƒ์„ฑํ•œ ์ž‘์„ฑ์ž์˜ ์ด๋ฆ„ (์˜ˆ : Steve)
startDate ์„ ํƒ String ๊ฒ€์ƒ‰ ์‹œ์ž‘ ๋‚ ์งœ (YYYY-MM-DD, ์˜ˆ : 2024-12-01)
endDate ์„ ํƒ String ๊ฒ€์ƒ‰ ์ข…๋ฃŒ ๋‚ ์งœ (YYYY-MM-DD, ์˜ˆ : 2024-12-02)
  • Query Parameters ์˜ˆ์‹œ
GET /workspaces/1/todos?startDate=2024-12-01&endDate=2024-12-31
  • RequestBody
ํ•„๋“œ ์ด๋ฆ„ ํ•„์ˆ˜ ์—ฌ๋ถ€ ํƒ€์ž… ์„ค๋ช…
empty empty empty empty
  • RequestBody ์˜ˆ์‹œ
empty

Response

๋”๋ณด๊ธฐ
  • ResponseBody
ํ•„๋“œ ์ด๋ฆ„ ํƒ€์ž… ์„ค๋ช…
workspace int ํ• ์ผ์ด ์†ํ•œ ์›Œํฌ์ŠคํŽ˜์ด์Šค id
todos List<Todo> ํ• ์ผ ๋ฆฌ์ŠคํŠธ
pagination Pagination ํŽ˜์ด์ง€ ์ •๋ณด
title string ํ• ์ผ ์ œ๋ชฉ
author string ํ• ์ผ ์ƒ์„ฑ์ž
...    
  • Response ์˜ˆ์‹œ
{
  "workspaceId": 1,
  "todos": [
    {
      "id": 1,
      "title": "Write monthly report",
      "author": "steve",
      "status": "done",
      "dueDate": "2024-12-15",
      "createdAt": "2024-12-01T08:30:00Z"
    },
    {
      "id": 2,
      "title": "Prepare presentation",
      "author": "john",
      "status": "done",
      "dueDate": "2024-12-20",
      "createdAt": "2024-12-05T09:00:00Z"
    }
  ],
  "pagination": {
    "currentPage": 1,
    "pageSize": 5,
    "totalPages": 2,
    "totalItems": 10
  }
}
  • Error Response
ํ•„๋“œ ์ด๋ฆ„ ์„ค๋ช…
error ์—๋Ÿฌ ์ด๋ฆ„
message ์—๋Ÿฌ ๋ฉ”์„ธ์ง€
  • Error Response ์˜ˆ์‹œ
{
  "error": "Invalid date range",
  "message": "The 'startDate' must be earlier than 'endDate'."
}

 

 

MVC - ๋ฉด์ ‘์—์„œ ๋ฌผ์–ด๋ณด๋Š” ๋‚ด์šฉ โญโญโญ

  • Model์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ์ฒด์ด๋‹ค.
  • ๊ฐ๊ฐ์˜ ์—ญํ• ์„ ํ™•์‹คํ•˜๊ฒŒ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๋‚ด ๋จธ๋ฆฌ ์†์— ์ž˜ ์ •๋ฆฌํ•ด๋‘๊ธฐ.. ๐Ÿคจ

๊ธฐ๋ณธ์ ์ธ MVC ์ˆœ์„œ

์„ธ๋ถ€์ ์ธ MVC ์ˆœ์„œ

 

 

MVC ์‹ค์Šต ์‹œ๋‚˜๋ฆฌ์˜ค : ํšŒ์› ๊ด€๋ฆฌ ํ”„๋กœ๊ทธ๋žจ

  • ํšŒ์› ๋ชฉ๋ก ์กฐํšŒ
    • ํšŒ์› ๋ชฉ๋ก ํŽ˜์ด์ง€
  • ํšŒ์› ์ƒ์„ฑ
    • ํšŒ์› ์ƒ์„ฑ ํŽ˜์ด์ง€
    • ํšŒ์› ์ƒ์„ฑ API

์˜์กด์„ฑ ์ถ”๊ฐ€

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

member.java

๋”๋ณด๊ธฐ
public class Member {

    // ์†์„ฑ
    private String name;  // ํšŒ์› ์ด๋ฆ„
    private String email;  // ํšŒ์› ์ด๋ฉ”์ผ
    private int age;  // ํšŒ์› ๋‚˜์ด

    // ์ƒ์„ฑ์ž


    // ๊ธฐ๋Šฅ
    // Getter
    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public int getAge() {
        return age;
    }

	// Setter
    public void setName(String name) {
        this.name = name;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

(HTML) member-create.html

๋”๋ณด๊ธฐ
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>New Member</title>
</head>
<body>
<h1>Create New Member</h1>
<form action="/members" method="post">
    <label>Name: <input type="text" name="name" /></label><br />
    <label>Email: <input type="email" name="email" /></label><br />
    <label>Age: <input type="number" name="age" /></label><br />
    <button type="submit">Create</button>
</form>
<a href="/members">Back to List</a>
</body>
</html>

(HTML) member-list.html

๋”๋ณด๊ธฐ
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Member List</title>
</head>
<body>
<h1>Member List</h1>
<a href="/members/new">Create New Member</a>
<table>
    <tr>
        <th>Name</th>
        <th>Email</th>
        <th>Age</th>
    </tr>
    <tr th:each="member : ${members}">
        <td th:text="${member.name}"></td>
        <td th:text="${member.email}"></td>
        <td th:text="${member.age}"></td>
    </tr>
</table>
</body>
</html>

MvcController.java

๋”๋ณด๊ธฐ
@Controller
@RequestMapping("/members")
public class MvcController {

    private final List<Member> memberList = new ArrayList<>();

    // ์ „์ฒด ํšŒ์› ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€ ์กฐํšŒ API
    @GetMapping
    public String getMemberListView(Model model) {
        List<Member> foundMemberList = this.getMemberList();
        model.addAttribute("members", foundMemberList);
        return "member/member-list";
    }

    // ํšŒ์› ์ƒ์„ฑ ํŽ˜์ด์ง€ ์กฐํšŒ API
    @GetMapping("/new")
    public String createMemberListView() {
        return "member/member-create";
    }

    // ํšŒ์› ์ƒ์„ฑ API
    @PostMapping
    public String createMemberAPI(@ModelAttribute Member member) {
        this.addMember(member);
        // return "member/memberlist";
        return "redirect:/members";
    }
}