[인프런 워밍업 클럽 1기] 첫번째 발자국

01_네트워크와 API


☾ Network

데이터를 주고 받는 것(=택배)

☾ IP

각 컴퓨터의 고유 주소(=집 주소)

256를 넘지 않는 4개의 숫자로 이루어짐

ex. 대한시 민국구 서울동

☾ Domain Name

IP는 외우기 어려우니까 외우기 쉽게 별칭을 달아준 것

ex. 빨간집

☾ port number

컴퓨터에서 돌아가는 여러 프로그램 중 한 프로그램을 특정하는 것

ex. 홍길동

☾ 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 요청을 받는 컴퓨터와 프로그램 정보

  • 정보를 보내는 2가지 방법

  1. Query : GET, DELETE에서 사용

  2. body : POST, PUT에서 사용

HTTP 응답

HTTP/1.1 200 OK
Content-Type: application/json

{
 "name":"A",
 "age":null
}

<aside> 💡 첫째줄 - 상태코드 여러줄 - 헤더 (한 줄 띄기) 여러줄 - 바디

</aside>

  • 상태코드

    • 200 OK : 정상적으로 수행

    • 300 Moved Permanently : 다른 곳으로 옮겨가라

    • 404 Not Found : 요청을 찾을 수 없다

    • 500 Internal Server Error : 내부에 문제가 생김

☾ API(Application Programming Interface)

정해진 약속을 하여 특정 기능을 수행하는 것

<aside> 💡 첫째줄 - Method path Query

여러줄 - 헤더(여러 줄 가능)

(한 줄 띄기)

여러줄 - 바디(여러 줄 가능)

</aside>

☾ URL(Uniform Resource Locator)

<http://spring.com:3000/portion?color=red&count=2>

주소창

  • http : 사용하고 있는 프로토콜

  • spring.com:3000 : 도메인 이름. 포트, 도메인 이름은 IP로 대체 가능

  • portion : path

  • color=red&count=2 : 쿼리. 추가 정보

02_GET API


Query를 사용하여 데이터 요청

☾ 덧셈 API 명세서

HTTP Method HTTP Path Query(key, value) API의 반환 결과 GET /add int number1, int number2 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 : 주어진 클래스를 Controller(API의 진입 지점)으로 만들어 준다. API를 개발하려고 하는 클래스에 작성. 이 어노테이션이 있어야 클래스 안에 함수를 만들고 API 호출됐을 때 이 함수로 연결
@RestController
public class CalculatorController {

    // @GetMapping("/add") : GET /add. 아래 함수를 HTTP Method가 GET이고 HTTP path가 /add인 API로 지정한다.
    @GetMapping("/add")
    public int addTwoNumbers(@RequestParam int number1, @RequestParam int number2) { // @RequestParam : Query를 통해서 넘어온 데이터를 함수 파라미터에 넣을 때 사용
        return number1 + number2;
    }
}

@RequestParam 문제점 : Query가 늘어나면 함수의 파라미터도 길어짐


☾ DTO

해결책 : 객체를 받아서 사용

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 : 주어진 클래스를 Controller(API의 진입 지점)으로 만들어 준다. API를 개발하려고 하는 클래스에 작성. 이 어노테이션이 있어야 클래스 안에 함수를 만들고 API 호출됐을 때 이 함수로 연결
@RestController
public class CalculatorController {

    // @GetMapping("/add") : GET /add. 아래 함수를 HTTP Method가 GET이고 HTTP path가 /add인 API로 지정한다.
    @GetMapping("/add")
    public int addTwoNumbers(CalculatorAddRequest request) { // API 호출될 때 주어진 Query가 이 객체에 있는 값에 들어가게 됨
        return request.getNumber1() + request.getNumber2();
    }
}

 

DTO(Data Transfer Object) : 정보를 전달하는 역할의 객체

03_POST API


HTTP body를 사용하여 데이터 받음

  • JSON(JavaScript Object Notation)

객체 표기법

HTTP body에서 사용됨

{
   "key": value
}

☾ 곱셈 API 명세서

HTTP Method HTTP Path HTTP Body API의 반환 결과 POST /multiply { “number1”:숫자, “number2”:숫자 } 숫자(곱셈 결과)

☾ 코드로 구현

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 : 주어진 클래스를 Controller(API의 진입 지점)으로 만들어 준다. API를 개발하려고 하는 클래스에 작성. 이 어노테이션이 있어야 클래스 안에 함수를 만들고 API 호출됐을 때 이 함수로 연결
@RestController
public class CalculatorController {

    @PostMapping("/multiply") // POST /multiply
    public int multiplyTwoNumbers(@RequestBody CalculatorMultiplyRequest request) { // @RequestBody : POST API에서 HTTP body 안에 담긴 json을 DTO 객체로 변환시킬 수 있음. DTO 객체 앞에 작성.
        return request.getNumber1() *request.getNumber2();
    }
}

 

04_유저 생성 API


☾ 도서관 사용자 등록 API 명세서

HTTP Method HTTP Path HTTP Body(JSON) 결과 반환 POST /user { “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 반환
}

05_유저 조회 API


☾ 유저 조회 API 명세서

HTTP Method HTTP Path Query 결과 반환 GET /user 없음 { “id”: Long, “name”: String(null 불가능), “age”: Integer }

  • Id : 유저 별로 겹치지 않는 유일한 번호(Primary Key). 여기서는 저장할 때 List에 담겨 있는 유저의 순서를 id로 지정

  • Controller에서 getter가 있는 객체를 반환하면 JSON이 된다.

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 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 {

    @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의 문제점

서버 재시작하면 데이터 사라짐

유저 정보가 메모리에서만 유지되고 있기 때문

 

 

일주일 간의 학습 내용에 대한 간단한 회고를 작성해 주세요.

API 제작을 어느 정도 알고 있다고 생각했는데 직접 해보니 아직 부족하다는 생각이 들었다. 복습 시간을 더 가져야겠다고 생각했다. 초반부에는 내용 정리를 꾸준히 하다가 점차 미루게 됐다. 주말에 남은 내용을 정리하고 다음주부터는 다시 일정에 맞게 내용 정리를 해야겠다.

 

 

미션을 해결하는 과정을 요약해 주세요.

  • 강의에서 작성한 코드와 주석을 읽으면서 내용을 복습하고, 이를 바탕으로 미션을 해결했다.

  • 다른 사람이 작성한 코드도 함께 읽어보며 미션을 했다.

     

     

    미션 해결에 대한 간단한 회고를 작성해 주세요.

API 만드는 미션이 생각보다 쉽지 않았다. 우선 제출은 했지만 추후에 수정할 필요가 있을 것 같다.

댓글을 작성해보세요.