REST API 만들기

Walker·2021년 4월 19일
0

WEB

목록 보기
1/1

전에 만들기로 했던 회원가입은 요즘 구직(코테, 과제, 면접 등) 때문에
우선순위가 좀 밀려난 상황이다.
지금은 시간과 에너지를 그 외에 쓰는 것이 부담스럽게 느껴지는게 사실이다.
그래도 나에게 포트폴리오는 어느 책에서 본 부숴도 괜찮은 장난감이라
나중에 부담없이 다루고 싶다.



REST API를 만들게 된 이유는 코딩테스트로도 접했고 과제로도 받았지만
정확하게 만들어본 경험이 없어 뭔가 계속 찜찜한 기분이 들었다.

구글링을 부족했는지는 몰라도 이론적인 내용이나 부분부분 내용들은 있었으나
딱 잡히는 뭔가가 없어서 두루뭉술하게 넘어갔던 것 같아 이번에 하나 만들어보게 되었다.


  • REST API 설계 시 가장 중요한 항목은 다음의 2가지
    첫 번째, URI는 정보의 자원을 표현해야 한다.
    두 번째, 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현한다.
    출처 : NHN Cloud 포스팅'(https://meetup.toast.com/posts/92)

기본적인 REST API의 큰 맥은 이 글에서부터 시작하게 되었다.
코드로 예를 들어 설명해보자면

@GetMapping("/movies")
public List<MovieInfo> getMovies() {
    return movieService.getMovies();
}

[GET]

위 코드는 "/movies" 라는 URI로 GET이라는 행위(method)로 표현된 요청이
들어오면 이를 Mapping하여 실행하겠다는 코드다.

여기에서 유의해야 할 점은 URI는 행위를 나타내서는 안되기 때문에
"getMovies"와 같이 동사 표현이 들어가서는 안된다는 것이다.
(URI명사로만하고, 행위(동사)는 Method로 분리하여 표현!)


그러면 이런 생각이 들었다.
"나는 그냥 URL만 웹브라우저 주소창에 입력했는데 get 요청은 언제 한거지?"
아래 글에 따르면 http 프로토콜 자체에 default 메소드는 없다고 한다. default가 get인줄
(https://forums.asp.net/t/2004670.aspx?What+is+the+default+Http+method+)

추측컨데 form 태그의 default 메소드가 get인것처럼
웹브라우저의 주소창이 form과 유사하게 default 메소드가 get이 아닐까 싶다.
(https://stackoverflow.com/questions/2314401/what-is-the-default-form-http-method)

아래 링크의 글에 따르면 "주소 표시줄에 직접 입력한 URL의 경우는 GET"이라고 한다.
(보통의 웹 브라우저라면 주소창의 default 메소드가 get인게 자연스럽긴 하다.)
(https://owlgwang.tistory.com/1)


@GetMapping("/movies/{movieId}")
public MovieInfo getMovie(@PathVariable("movieId") int movieId) {
    return movieService.getMovie(movieId);
}

처음의 GET 요청에서는 있는 정보를 다 읽어오기 때문에 딱히 파라미터가 필요없었으나
위의 경우처럼 특정 정보를 가져오기 위해서는 특정 정보의 식별자가 필요하다

ReadAll
SELECT *
FROM   movie_info

ReadOne
SELECT *
FROM   movie_info
WHERE  movie_id = 3

즉 WHERE절의 인자가 필요한 것이다.
@PathVariableURL의 특정 부분을 파라미터로 받아올 수 있는 어노테이션으로
@RequestParam이 QueryString(URL?a=1&b=2)의 value(1,2)를 받아오는 것과 달리
특정 부분을 그대로 받아 파라미터로 사용한다(ex) "/movies/3" -> 3)


 @PostMapping("/movies")
 public void insertMovie(@RequestBody MovieInfo movieInfo) {
     movieService.insertMovie(movieInfo);
 }

[POST]

POST도 GET과 기본적인 규칙은 같지만
위 코드에서 보다시피 @RequestBody라는 어노테이션을 확인할 수 있다.
@RequestBody는 HTTP의 요청의 Body 부분을 Java 객체로 Mapping 해준다.

좀 더 직관적으로 PostMan(API로 요청을 보내는 용도)이라는 서비스로 예로 들자면
위와 같이 "/api/movies"라는 URI와 왼쪽의 POST 메소드 선택으로 요청을 보내면서
Body 부분에 json 형태의 Data를 넣어서 보내주고 있다.

public class MovieInfo {
    private int movieId;
    private String movieName;
    private String movieGenre;
    private int movieRunningTime;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date movieOpeningDate;
    private Double movieRating;
    private String movieActors;
    private String[] movieActorsArray;
}

PostMan에서 보낸 json 데이터가 위와 같은
Java 객체(MovieInfo(dto))에 Mapping 되는 것이다.
movieId는 MySQL에서 AutoIncrement로 생성되는 값이고,
movieActors의 경우 MySQL은 배열 저장을 지원하지 않는다 하여
일단 String으로 저장한 후에 사용시에 ","(콤마) 기준으로 분리한다.
(PostgreSQL은 배열을 저장 할 수 있다고 한다.
MySQL도 json 형태로 가능하다는데 많이 쓰지는 않는듯...)


@PatchMapping("/movies/{movieId}")
public void updateMovie(@PathVariable("movieId") int movieId, @RequestBody MovieInfo movieInfo) {
    movieInfo.setMovieId(movieId);
    movieService.updateMovie(movieInfo);
}

[PUT(PATCH)]

PUT의 경우 이 글(https://papababo.tistory.com/entry/HTTP-METHOD-PUT-vs-PATCH-%EC%B0%A8%EC%9D%B4%EC%A0%90)을 보고 대신 PATCH를 사용하게 되었는데
PUT보내지 않은 값들은 null이나 초기값 처리한다하여 위험하게 생각되었다.

<set>
    <if test="movieName != null">movie_name=#{movieName},</if>
</set>

물론 위와 같이 Mapper에서 받은 값들을 확인하여 동적으로 쿼리를 만들어
이러한 위험을 막을 수도 있겠지만 굳이 위험을 감수할 필요는 없다는 판단이다.

PUT과 PATCH를 비교하는 글(https://woowacourse.github.io/javable/post/2020-08-17-put-vs-patch) 중에서 PATCH는 PUT처럼 완전히 대체하는 것이 아니라 멱등성이 없다는 내용도 있는데 그에 대한 글(https://www.inflearn.com/questions/110644)을 보고 어떤 연산이 들어가는 것이 아니라 그냥 값을 변경하는 것이라면 크게 걱정할 부분은 아니라는 생각이 들었다.

코드에서는 @PathVariable("movieId") int movieId 부분에서 수정할 Data row를 특정하고
@RequestBody MovieInfo movieInfo를 통해 받아온 수정값을 대입하는 방식이다.


@DeleteMapping("/movies/{movieId}")
public void deleteMovie(@PathVariable("movieId") int movieId) {
    movieService.deleteMovie(movieId);
}

[DELETE]

마지막으로 DELETE는 GET과 유사해보이고 사실 GET으로 사용해도 기능상에 큰 차이는 없을 수 있으나 Google의 Accelerator 사건과 같이
GET과 DELETE를 혼용하다 Data가 삭제되는 참사가 벌어질 수 있으니
되도록 구분해서 사용하는 것이 좋겠다는 생각이다.


ps. 부정확할 수도 있지만 Sample Code가 필요하시면 https://github.com/walker0625/REST_API 에 코드를 올려놓았으니 편하게 참고하시고 틀린 부분이 있으면 피드백 감사히 받겠습니다!

profile
I walk slowly, but I never walk backward. -Abraham Lincoln-

0개의 댓글