1. REST API의 탄생

REST는 Representational State Transfer라는 용어의 약자로서 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 최초로 소개되었습니다. 로이 필딩은 HTTP의 주요 저자 중 한 사람으로 그 당시 웹(HTTP) 설계의 우수성에 비해 제대로 사용되어지지 못하는 모습에 안타까워하며 웹의 장점을 최대한 활용할 수 있는 아키텍처로써 REST를 발표했다고 합니다.

 

2. REST 구성

쉽게 말해 REST API는 다음의 구성으로 이루어져있습니다. 자세한 내용은 밑에서 설명하도록 하겠습니다.

  • 자원(RESOURCE) - URI
  • 행위(Verb) - HTTP METHOD
  • 표현(Representations)

 

3. REST 의 특징

1) Uniform (유니폼 인터페이스)

Uniform Interface는 URI로 지정한 리소스에 대한 조작을 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일을 말합니다.

 

2) Stateless (무상태성)

REST는 무상태성 성격을 갖습니다. 다시 말해 작업을 위한 상태정보를 따로 저장하고 관리하지 않습니다. 세션 정보나 쿠키정보를 별도로 저장하고 관리하지 않기 때문에 API 서버는 들어오는 요청만을 단순히 처리하면 됩니다. 때문에 서비스의 자유도가 높아지고 서버에서 불필요한 정보를 관리하지 않음으로써 구현이 단순해집니다.

 

3) Cacheable (캐시 가능)

REST의 가장 큰 특징 중 하나는 HTTP라는 기존 웹표준을 그대로 사용하기 때문에, 웹에서 사용하는 기존 인프라를 그대로 활용이 가능합니다. 따라서 HTTP가 가진 캐싱 기능이 적용 가능합니다. HTTP 프로토콜 표준에서 사용하는 Last-Modified태그나 E-Tag를 이용하면 캐싱 구현이 가능합니다.

 

4) Self-descriptiveness (자체 표현 구조)

REST의 또 다른 큰 특징 중 하나는 REST API 메시지만 보고도 이를 쉽게 이해 할 수 있는 자체 표현 구조로 되어 있다는 것입니다.

 

5) Client - Server 구조

REST 서버는 API 제공, 클라이언트는 사용자 인증이나 컨텍스트(세션, 로그인 정보)등을 직접 관리하는 구조로 각각의 역할이 확실히 구분되기 때문에 클라이언트와 서버에서 개발해야 할 내용이 명확해지고 서로간 의존성이 줄어들게 됩니다.

 

6) 계층형 구조

REST 서버는 다중 계층으로 구성될 수 있으며 보안, 로드 밸런싱, 암호화 계층을 추가해 구조상의 유연성을 둘 수 있고 PROXY, 게이트웨이 같은 네트워크 기반의 중간매체를 사용할 수 있게 합니다.

 

 

4. REST API 중심 규칙

REST에서 가장 중요하며 기본적인 규칙은 아래 두 가지입니다.

  • URI는 정보의 자원을 표현해야 한다.
  • 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE 등)으로 표현한다.

 

1번 사용자에 대해 정보를 받아야 할 때를 예를 들면, 아래와 같은 방법은 좋지 않습니다.

GET /users/show/1

 

이와 같은 URI 방식은 REST를 제대로 적용하지 않은 구 버전의 Rails에서 흔히 볼 수 있는 URL입니다. 이 URI은 자원을 표현해야 하는 URI에 /show/ 같은 불필요한 표현이 들어가 있기 때문에 적절하지 않습니다. 본다는 것은 GET이라는 HTTP Method로 충분히 표현할 수 있기 때문이죠. 최근의 Rails는 아래와 같이 변경되었습니다.

GET /users/1

 

자원은 크게 Collection과 Element로 나누어 표현할 수 있으며, 아래 테이블에 기초한다면 서버 대부분과의 통신 행태를 표현할 수 있습니다.

 

Resource GET PUT POST DELETE
Collection URI,
such as 
http://example.com/resources/
컬렉션에 속한 자원들의 RI나 그 상세사항의 목록을 보여준다. 전체 컬렉션은 다른 컬렉션으로 교체한다. 해당 컬렉션에 속하는 새로운 자원을 생성한다. 자원의 URI는 시스템에 의해 할당된다. 전체 컬렉션을 삭제한다.
Element URI,
such as 
http://example.com/resources/item17
요청한 컬렉션 내 자원을 반환한다. 해당 자원을 수정한다. 해당 자원에 귀속되는 새로운 자원을 생성한다. 해당 컬렉션내 자원을 삭제한다.
  • 이 외에도 PATCH 라는 HTTP Method에도 주목하시기 바랍니다. PUT이 해당 자원의 전체를 교체하는 의미를 지니는 대신, PATCH는 일부를 변경한다는 의미를 지니기 때문에 최근 update 이벤트에서 PUT보다 더 의미적으로 적합하다고 평가받고 있습니다. Rails도 4.0부터 PATCH가 update 이벤트의 기본 Method로 사용될 것이라 예고하고 있습니다.

 

4-1. REST API 중심 규칙

1) URI는 정보의 자원을 표현해야 한다. (리소스명은 동사보다는 명사를 사용)

    GET /members/delete/1

위와 같은 방식은 REST를 제대로 적용하지 않은 URI입니다. URI는 자원을 표현하는데 중점을 두어야 합니다. delete와 같은 행위에 대한 표현이 들어가서는 안됩니다.

 

2) 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE 등)로 표현

위의 잘못 된 URI를 HTTP Method를 통해 수정해 보면

    DELETE /members/1

으로 수정할 수 있겠습니다.
회원정보를 가져올 때는 GET, 회원 추가 시의 행위를 표현하고자 할 때는 POST METHOD를 사용하여 표현합니다.

회원정보를 가져오는 URI

    GET /members/show/1     (x)
    GET /members/1          (o)

회원을 추가할 때

    GET /members/insert/2 (x)  - GET 메서드는 리소스 생성에 맞지 않습니다.
    POST /members/2       (o)

 

POST, GET, PUT, DELETE 이 4가지의 Method를 가지고 CRUD를 할 수 있습니다.

 

METHOD역할

 

POST POST를 통해 해당 URI를 요청하면 리소스를 생성합니다.
GET GET를 통해 해당 리소스를 조회합니다. 리소스를 조회하고 해당 도큐먼트에 대한 자세한 정보를 가져온다.
PUT PUT를 통해 해당 리소스를 수정합니다.
DELETE DELETE를 통해 리소스를 삭제합니다.

 

다음과 같은 식으로 URI는 자원을 표현하는 데에 집중하고 행위에 대한 정의는 HTTP METHOD를 통해 하는 것이 REST한 API를 설계하는 중심 규칙입니다.

 

4-2. URI 설계 시 주의할 점

1) 슬래시 구분자(/)는 계층 관계를 나타내는 데 사용

    http://restapi.example.com/houses/apartments
    http://restapi.example.com/animals/mammals/whales

 

2) URI 마지막 문자로 슬래시(/)를 포함하지 않는다.

URI에 포함되는 모든 글자는 리소스의 유일한 식별자로 사용되어야 하며 URI가 다르다는 것은 리소스가 다르다는 것이고, 역으로 리소스가 다르면 URI도 달라져야 합니다. REST API는 분명한 URI를 만들어 통신을 해야 하기 때문에 혼동을 주지 않도록 URI 경로의 마지막에는 슬래시(/)를 사용하지 않습니다.

    http://restapi.example.com/houses/apartments/ (X)
    http://restapi.example.com/houses/apartments  (0)

 

3) 하이픈(-)은 URI 가독성을 높이는데 사용

URI를 쉽게 읽고 해석하기 위해, 불가피하게 긴 URI경로를 사용하게 된다면 하이픈을 사용해 가독성을 높일 수 있습니다.

 

4) 밑줄(_)은 URI에 사용하지 않는다.

글꼴에 따라 다르긴 하지만 밑줄은 보기 어렵거나 밑줄 때문에 문자가 가려지기도 합니다. 이런 문제를 피하기 위해 밑줄 대신 하이픈(-)을 사용하는 것이 좋습니다.(가독성)

 

5) URI 경로에는 소문자가 적합하다.

URI 경로에 대문자 사용은 피하도록 해야 합니다. 대소문자에 따라 다른 리소스로 인식하게 되기 때문입니다. RFC 3986(URI 문법 형식)은 URI 스키마와 호스트를 제외하고는 대소문자를 구별하도록 규정하기 때문이지요.

    RFC 3986 is the URI (Unified Resource Identifier) Syntax document

 

6) 파일 확장자는 URI에 포함시키지 않는다.

    http://restapi.example.com/members/soccer/345/photo.jpg (X)

REST API에서는 메시지 바디 내용의 포맷을 나타내기 위한 파일 확장자를 URI 안에 포함시키지 않습니다. Accept header를 사용하도록 합시다.

    GET / members/soccer/345/photo HTTP/1.1 Host: restapi.example.com Accept: image/jpg

 

4-3. 리소스 간의 관계를 표현하는 방법

REST 리소스 간에는 연관 관계가 있을 수 있고, 이런 경우 다음과 같은 표현방법으로 사용합니다.

    /리소스명/리소스 ID/관계가 있는 다른 리소스명

    ex)    GET : /users/{userid}/devices (일반적으로 소유 ‘has’의 관계를 표현할 때)

만약에 관계명이 복잡하다면 이를 서브 리소스에 명시적으로 표현하는 방법이 있습니다. 예를 들어 사용자가 ‘좋아하는’ 디바이스 목록을 표현해야 할 경우 다음과 같은 형태로 사용될 수 있습니다.

    GET : /users/{userid}/likes/devices (관계명이 애매하거나 구체적 표현이 필요할 때)

 

4-4. 자원을 표현하는 Colllection과 Document

Collection과 Document에 대해 알면 URI 설계가 한 층 더 쉬워집니다. DOCUMENT는 단순히 문서로 이해해도 되고, 한 객체라고 이해하셔도 될 것 같습니다. 컬렉션은 문서들의 집합, 객체들의 집합이라고 생각하시면 이해하시는데 좀더 편하실 것 같습니다. 컬렉션과 도큐먼트는 모두 리소스라고 표현할 수 있으며 URI에 표현됩니다. 예를 살펴보도록 하겠습니다.

    http:// restapi.example.com/sports/soccer

위 URI를 보시면 sports라는 컬렉션과 soccer라는 도큐먼트로 표현되고 있다고 생각하면 됩니다. 좀 더 예를 들어보자면

    http:// restapi.example.com/sports/soccer/players/13

sports, players 컬렉션과 soccer, 13(13번인 선수)를 의미하는 도큐먼트로 URI가 이루어지게 됩니다. 여기서 중요한 점은 컬렉션은 복수로 사용하고 있다는 점입니다. 좀 더 직관적인 REST API를 위해서는 컬렉션과 도큐먼트를 사용할 때 단수 복수도 지켜준다면 좀 더 이해하기 쉬운 URI를 설계할 수 있습니다.

 

 

 

5. HTTP 응답 상태 코드

마지막으로 응답 상태코드를 간단히 살펴보도록 하겠습니다. 잘 설계된 REST API는 URI만 잘 설계된 것이 아닌 그 리소스에 대한 응답을 잘 내어주는 것까지 포함되어야 합니다. 정확한 응답의 상태코드만으로도 많은 정보를 전달할 수가 있기 때문에 응답의 상태코드 값을 명확히 돌려주는 것은 생각보다 중요한 일이 될 수도 있습니다. 혹시 200이나 4XX관련 특정 코드 정도만 사용하고 있다면 처리 상태에 대한 좀 더 명확한 상태코드 값을 사용할 수 있기를 권장하는 바입니다.

 

 

상태코드

200 클라이언트의 요청을 정상적으로 수행함
201 클라이언트가 어떠한 리소스 생성을 요청, 해당 리소스가 성공적으로 생성됨(POST를 통한 리소스 생성 작업 시)

 

상태코드

400 클라이언트의 요청이 부적절 할 경우 사용하는 응답 코드
401 클라이언트가 인증되지 않은 상태에서 보호된 리소스를 요청했을 때 사용하는 응답 코드
  (로그인 하지 않은 유저가 로그인 했을 때, 요청 가능한 리소스를 요청했을 때)
403 유저 인증상태와 관계 없이 응답하고 싶지 않은 리소스를 클라이언트가 요청했을 때 사용하는 응답 코드
  (403 보다는 400이나 404를 사용할 것을 권고. 403 자체가 리소스가 존재한다는 뜻이기 때문에)
405 클라이언트가 요청한 리소스에서는 사용 불가능한 Method를 이용했을 경우 사용하는 응답 코드

 

상태코드

301 클라이언트가 요청한 리소스에 대한 URI가 변경 되었을 때 사용하는 응답 코드
  (응답 시 Location header에 변경된 URI를 적어 줘야 합니다.)
500 서버에 문제가 있을 경우 사용하는 응답 코드

 

 


참고

 

https://spoqa.github.io/2012/02/27/rest-introduction.html

https://meetup.toast.com/posts/92

https://sanghaklee.tistory.com/57

 

'Coding > 기타' 카테고리의 다른 글

HTTP 이해  (0) 2022.12.06
기술매니저님 추천 사이트 ( Spring 관련 )  (0) 2022.12.02
Error : Address already in use 오류 해결하기  (0) 2022.11.04
피그마 figma  (0) 2022.10.27
JWT 관련 필요한 용어 정리  (0) 2022.10.26

시험장 앞에 두 줄로 서있는게 왜이렇게 귀엽냐면서

 

오늘 지난 1주일 동안 과제를 하면서 배웠던 내용을 토대로 주특기 시험을 봤다. 

시험 난이도는 이번주 과제를 마쳤더라면 누구나 할 수 있을정도로 쉬웠지만, 하면서 예기치않은 오류를 봐서 당황 ㅎㅎ

이번 글 마지막에 처음 봤던 오류 메시지 + 시도했던 방법들도 같이 정리해보려 한다.

+ 같이 오류를 해결해주신 모든 분들 ( 은솔님, 소영님, 미경님, 윤재님,  ...) 감사합니다 !!!

+ 그리고 코드 전체 주석을 달고 하나씩 풀어보면서 해결해 보라고 팁을 주신 지성님도 감사합니다 !!! 


시험 과제

 

 

 

 

작성한 코드

제출해야하는 과제는 조회기능인 GET이지만, 테스트를 위해 POST부분 까지 같이 작성했다.

 

1. Controller 

 

2. DTO

 

RequestDto

ResponseDto

 

 

3. Service

 

POST

GET

 

 

4. Entity

 

5. Repository

 

h2 - console

구현해야 하는 대로 잘 작성이 되는 것을 볼 수 있다.

 

 

 

 


 

트러블 슈팅 1 - 인텔리제이 run 실행 버튼이 활성화 되지 않았던 문제

 

1. 발생한 문제 ( 아래는 콘솔창에 떴던 에러 메세지이다.)

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberController' defined in file [/Users/chaleejin/Desktop/hanghaetest/out/production/classes/com/sparta/hanghaetest/controller/MemberController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberService' defined in file [/Users/chaleejin/Desktop/hanghaetest/out/production/classes/com/sparta/hanghaetest/service/MemberService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'memberRepository' defined in com.sparta.hanghaetest.repository.MemberRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.List com.sparta.hanghaetest.repository.MemberRepository.findAllByOrderByModifiedAtDesc(); Reason: Failed to create query for method public abstract java.util.List com.sparta.hanghaetest.repository.MemberRepository.findAllByOrderByModifiedAtDesc()! No property 'modifiedAt' found for type 'Member'; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List com.sparta.hanghaetest.repository.MemberRepository.findAllByOrderByModifiedAtDesc()! No property 'modifiedAt' found for type 'Member'

 

너무 길어서 앞부분만 보고 자세히 보지 않았는데, 꼼꼼히 봤더라면 해결되었을 문제였다.

아래는 위의 에러 내용이다. 빨간색 부분으로 에러가 발생했던 주요 이유를 표시해보았다.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberController' defined in file [/Users/chaleejin/Desktop/hanghaetest/out/production/classes/com/sparta/hanghaetest/controller/MemberController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberService' defined in file [/Users/chaleejin/Desktop/hanghaetest/out/production/classes/com/sparta/hanghaetest/service/MemberService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'memberRepository' defined in com.sparta.hanghaetest.repository.MemberRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.List com.sparta.hanghaetest.repository.MemberRepository.findAllByOrderByModifiedAtDesc(); Reason: Failed to create query for method public abstract java.util.List com.sparta.hanghaetest.repository.MemberRepository.findAllByOrderByModifiedAtDesc()! No property 'modifiedAt' found for type 'Member'; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List com.sparta.hanghaetest.repository.MemberRepository.findAllByOrderByModifiedAtDesc()! No property 'modifiedAt' found for type 'Member'

 

2. 시도한 방법

 

2-1. Preferences - Build - Build and run using 설정 Gradle 에서 IntelliJ IDEA로 바꿔주기

 

2-2. 해당 파일이 들어있는 디렉토리(파일)에서 물리적으로 out폴더를 삭제하고, 인텔리제이에서 Build - Rebuild 해주기

 

2-3. 인텔리제이 파일 - Invaildate Cashes - 모든 사항 체크 후 - 캐쉬삭제 해주기

 

 

2-4 인텔리제이에서 파일을 열 때, build.gradle 파일로 열어주기.

 

2-5 Application 파일을 다른 디렉토리로 이동했다가 다시 원위치 시켜보기

 

위 방법 모두 통하지 않았다. 왜냐, 근본 적인 문제가 아니었기 때문 ^^ 

나는 단지 실행버튼이 활성화 되지 않는 것에만 집중해서 구글링을 했고, 다른 분들도 그 부분으로 도와주셨는데, 에러 메세지의 맨 끝 부분에서 확인할 수 있듯이 사실은 상속에 관한 문제였다.

 

 

3. 해결한 방법

알고보니 Entity Member 클래스에서 Timestamped 클래스 상속을 해주지 않아서 발생했던 문제였다......

바부 ...... ㅎㅎㅎㅎ 도움을 주신 모든 분들에게 다시 한번 감사의 말씀을 전합니다 ㅎㅎㅎ !! 

 

트러블 슈팅 2 - Web server failed to start. Port 8080 was already in use.!

 

1. 발생한 문제 

 

2. 해결 방법 (구글링 짱 !)

 

  • 인텔리제이 콘솔창에 아래 코드 입력 후 엔터
lsof -n -i -P | grep 8080

  • 그럼 현재 켜져있는 포트의 pid 넘버가 나온다 

  • 다음 코드를 입력해서 포트 프로세스를 종료시켜 준다.
kill -9 1111(이건 임의의 번호입니다)

 

참고 : https://deeplify.dev/back-end/spring/port-is-already-in-use

 

[스프링/Spring] Port 8080 is already in use 에러 해결 방법

스프링에서 가끔 발생하는 8080 포트는 이미 사용중이라는 에러에 대해서 알아보고 해결하는 방법까지 알아보도록 하겠습니다.

deeplify.dev

 

🔐 JPA 가 무엇인가요?

  • Java 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용하는 인터페이스 모음
  • 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
  • 인터페이스 이기 때문에 Hibernate, OpenJPA 등이 JPA를 구현함
  • 참고로, JPA는 수정 메소드를 제공하지 않는다. 하지만 당연히 수정은 필요하기 때문에 JPA는 데이터 수정시, 매핑된 객체(테이블 데이터)를 조회해서 값을 변경 후 커밋하면 DB 서버에 UPDATE 문을 전송하여 UPDATE를 실행한다.
  • 추가적으로 알아둬야 할 것은, 스프링에서 흔히 사용하는 것으로 알고있는 JPA는, JPA를 이용하는 spring-data-jpa 프레임워크이지 JPA는 아니다.

 

🔐 Controller, Service, Repository 가 무엇인가요?

 

< 간단 정리 >

  • 컨트롤러 : @Controller (프레젠테이션 레이어, 웹 요청과 응답을 처리함)
  • 로직 처리 : @Service (서비스 레이어, 내부에서 자바 로직을 처리함)
  • 외부I/O 처리 : @Repository (퍼시스턴스 레이어, DB나 파일같은 외부 I/O 작업을 처리함)

Controller

  • Spring Framework의 Controller는 사용자가 화면(View)단에서 입력이나 어떤 이벤트를 했을 경우, 그 이벤트에 맞는 화면(View)이나 비지니스 로직(Model)을 실행할 수 있도록 업데이트 해주는 역할을 한다. 즉, Model과 View를 연결해주는 다리 역할이라고 할 수 있다. 
  • 서버에서 기능별 URL이라는 API를 개설해 놓았고, 클라이언트는 필요한 정보를 얻기 위해 적절한 API에 요청한다. Controller는 이런 창구 역할을 하는 API들을 모아놓은 클래스라고 할 수 있다.
  • Front-end에서 들어오는 클라이언트 측의 요청이 가장 먼저 서버 측과 맞닿는 부분
  • Client의 요청을 받았을 때 그 요청에 대해 실제 업무를 수행하는 Service를 호출
  • 클라이언트가 보낸 데이터가 있다면 Service를 호출할 때 전달하기 쉽게 데이터의 가공
  • 모델의 업무 수행이 완료되면 그 결과를 바탕으로 화면을 구성하도록 View에 전달
  • @Controller 어노테이션을 사용하여 만들어진 컨트롤러 클래스에 라우팅(Routing)할 수 있도록 요청 URL에 대해 해당하는 메소드를 매핑해줄 수 있도록 하기 위해 @RequestMapping 어노테이션을 사용한다.

Service

  • Service : Controller의 요청을 받아 알맞은 정보를 가공 Controller에게 재전달한다.
  • Service는 위에서 언급했듯이 Repository에서 얻어온 정보를 바탕으로 자바 문법을 이용하여 가공 후 다시 Controller에게 정보를 보내는 곳이다.
  • 클라이언트 즉 controller 쪽에서 바로 데이터베이스에 접근하여 정보를 얻고 가공해서 가져가는 것은 위험하다.
  • 정보를 직접 CRUD하고 가공하는 과정에서 테이블에 저장된 원본의 정보가 손상될 우려가 크기 때문이다.
  • 따라서 정보 변동의 위험이 큰 로직은 Service에서 진행한다.
  • 추가로 이때 원본의 데이터를 사용하는 것이 아니라 데이터베이스에서 추출한 정보의 복사본인 DTO를 만들어서 로직을 조작한다.

Repository

  • Repository는 직역해도 '저장소'로 데이터베이스와 깊은 연관이 있음을 알 수 있다.
  • 데이터단에 직접 매칭되는 Entity라는 것이 있는데, 이 Entity를 통해 데이터 테이블이 생성이 되면, 받아온 정보를 데이터베이스(ex. MySQL, mariaDB)에 저장하고 조회하는 기능을 수행한다.
  • Repository : Entity에 의해 생성된 DB에 접근하는 메서드를 사용하기위한 interface이다.
  • JPA를 상속받음으로써 기본적인 CRUD의 동작(함수 사용)이 가능해진다.
  • JpaRepository<대상 엔티티, Entity에 접근할 객체의 Type>

 

🔐 restAPI의 put 과 patch 는 어떤 차이점이 있을까요? 어떤 경우에 사용하면 좋을까요?

 

PUT

 

자원를 변경할 때에 사용하는 방식이다. CRUD에서 U. Update에 해당하며, DB에서도 Update에 해당한다.

참고로 PUT은 자원을 모두 대체(replace)하는 메소드이다. 요청한 URL 아래에 해당 자원이 존재하지 않으면 POST와 마찬가지로 새로운 자원으로써 저장한다. 만약 요청한 URL 아래에 해당 자원이 존재하면 기존에 존재하던 자원을 새롭게 요청으로 들어간 정보로 자원 전체를 대체한다. 만약 PUT을 사용하여 일부만 변경하고자 하여 자원의 전체 상태를 완전하지 못한 상태로 전송한다면 일부가 null값으로 변경될 수 있다.

 

 

PATCH

 

PATCH 역시 데이터를 변경할 때 사용한다. 하지만 PUT과 PATCH는 서로 대체제 관계가 아니며, 다른 정의와 규약을 가지고 있다. PATCH 요청은 자원에 대한 부분적인 수정을 적용하기 위한 메소드이다. 따라서 필요한 정보에 대해서만 요청할 수 있다.

 

 

 🔑  스프링 Bean이란?

스프링의 특징에는 제어의 역전(IoC / Invention Of Control)이 있다.

제어의 역전이란, 간단히 말해서 객체의 생성 및 제어권을 사용자가 아닌 스프링에게 맡기는 것이다. 객체에 IoC가 적용된 경우에는 이러한 객체의 생성과 사용자의 제어권을 스프링에게 넘기게 되며 스프링의 DI(Dependency Injection) Container에 의하여 관리 당하는 자바 객체를 사용자는 사용하게 된다. 이 객체를 '빈(bean)'이라 한다.

 

 

 

🔑  RESTful 하다?

REST의 정의

 

Representational State Transfer의 약자자원의 이름(자원의 표현)으로 구분하여 자원의 상태(정보)를 주고 받는 모든 것을 의미

 

1. HTTP URL을 통해 자원(Resource)를 명시하고

2. HTTP Method(POST, GET, PUT, DELETE)를 통해

3. 해당 자원에 대한 'CRUD Operation'을 적용하는 것을 의미한다.

 

REST의 구성 요소

 

1.자원(Resource) : HTTP URL

  • 모든 자원에 고유한 ID가 존재하고, 이 자원은 서버에 존재한다.
  • 자원을 구별하는 ID는 '/groups/:group_id'와 같은 HTTP URL이다.
  • Client는 URL을 이용해서 자원을 지정하고 해당 자원의 상태(정보)에 대한 조작을 서버에 요청한다.

2.행위(Verb) : HTTP Method

  • HTTP 프로토콜의 Method를 사용한다.
  • HTTP 프로토콜은 GET, POST, PUT, DELETE와 같은 메서드를 제공한다.

3.표현(Representations) : HTTP Message Pay Load

  • 클라이언트가 자원의 상태(정보)에 대한 조작을 요청하면 서버는 이에 적절한 응답(Representation)을 보낸다.
  • REST에서 하나의 자원은 JSON, XML, TEXT, RSS등 여러 형태의 Representation으로 나타내어 질 수 있다.
  • JSON 혹은 XML을 통해 데이터를 주고 받는 것이 일반적이다.

 

RESTful 이란 ?

  1. RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어이다.즉, 'REST API'를 제공하는 웹 서비스를 'RESTful'하다고 할 수 있다.
  2. RESTful은 REST를 REST답게 쓰기 위한 방법으로, 누군가가 공식적으로 발표한 것은 아니다.즉, REST 원리를 따르는 시스템은 RESTful이란 용어로 지칭된다.
  • RESTful 하지 못한 경우
  1. CRUD 기능을 모두 POST로만 처리하는 API
  2. REST API의 설계 규칙을 지키지 못한 경우
  • REST API 설계 규칙
  1. URL는 동사보다는 명사를, 대문자보다는 소문자를 사용해야 한다.
  2. 마지막에 슬래시(/)를 포함하지 않는다.
  3. 언더바 대신 하이폰을 사용한다.
  4. 파일확장자는 URL에 포함하지 않는다.
  5. 행위를 포함하지 않는다.

 

 

🔑  의존성이란?

사용하는 방법을 보면 라이브러리 == dependency ? 라는 의문이 생긴다. 라이브러리를 사용하는데 왜 dependency를 추가하는 것이라고 할까. 클래스 A가 다른 클래스 또는 인터페이스 B를 사용할 때 A가 B에 의존하는 관계가 된다. B가 없으면 작동할 수 없는 A를 dependant라고 하며, 의존의 대상이 되는 B를 dependency라고 한다. 또한 의존대상 B가 변하면 그것이 A에 영향을 미친다.

코드를 짤 때 라이브러러의 기능을 작성한 클래스가 사용하게 되면 라이브러리에 의존하게 되므로 라이브러리는 dependency가 되며, 작성한 클래스는 dependant가 된다.

그래서 dependency와 라이브러리가 같은 것 이라고 할 수 없지만 라이브러리를 사용하는 것이 dependency 즉, 의존성을 늘리게 되는 행동이기 때문에 라이브러리를 사용하는 것을 dependency를 추가한다고 표현하는 것이다.

 

 

 🔑  @Transactional

Transaction ? 

  • 데이터베이스의 상태를 변경하는 작업 또는 한번에 수행되어야 하는 연산들을 의미한다.
  • begin, commit 을 자동으로 수행해준다.
  • 예외 발생 시 rollback 처리를 자동으로 수행해준다.

 

Transaction 4 가지 성실

  • 원자성(Atomicity) : 한 트랜잭션 내에서 실행한 작업들은 하나의 단위로 처리한다. 즉, 모두 성공 또는 모두 실패.
  • 일관성(Consistency) : 트랜잭션은 일관성 있는 데이타베이스 상태를 유지한다. (data integrity 만족 등.)
  • 격리성(Isolation) : 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리해야한다.
  • 영속성(Durability) : 트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 한다.

 

Transaction 처리 방법

 

스프링에서는 간단하게 어노테이션 방식으로 @Transactional을 메소드, 클래스, 인터페이스 위에 추가하여 사용하는 방식이 일반적이다. 이 방식을 선언적 트랜잭션이라 부르며, 적용된 범위에서는 트랜잭션 기능이 포함된 프록시 객체가 생성되어 자동으로 commit 혹은 rollback을 진행해준다.

 

 

 

🔑  DAO, DTO, VO 란?

 

< 간단 정리>

  • DAO : 이름 그대로 DB에 접근하기 위한 객체이다. 서비스와 DB를 연결하기 위한 중간 다리의 역할을 한다. Mybatis를 기준으로 예시를 작성한다.
  • DTO : 말 그대로 '데이터 전송 객체'이다. 뷰에서 컨트롤러로 넘어오는 데이터를 담거나, 컨트롤러에서 서비스로 넘기는 데이터를 담거나 할 때 사용할 수 있다. 로직을 가지지 않고, 데이터 객체에 대한 정보만 담고 있다.
  • VO : VO는 값 그 자체를 나태는 객체이다. DTO와 반대로 로직을 포함할 수 있으며, VO의 경우 특정 값 자체를 표현하기 때문에 불변성의 보장을 위해 생성자를 사용하여야 한다. 서로 다른 이름을 갖는 VO 인스턴스라도 모든 속성 값이 같다면 두 인스턴스는 같은 객체인 것이 핵심이다.

 

DAO

  • DAO(Data Access Object) 는 데이터베이스의 data에 접근하기 위한 객체. DataBase에 접근 하기 위한 로직 & 비지니스 로직을 분리하기 위해 사용한다.
  • DAO의 경우는 DB와 연결할 Connection 까지 설정되어 있는 경우가 많다.
  • 그래서 현재 많이 쓰이는 Mybatis 등을 사용할 경우 커넥션풀까지 제공되고 있기 때문에 DAO를 별도로 만드는 경우는 드물다.
  • DAO는 오버헤드를 줄이기 위해 사용된다. 오직 1개의 커넥션만을 이용하여 다수의 사용자들의 생성, 조회, 수정 , 삭제 등의 DB I/O역할을 모두 수행하기 때문에 애플리케이션의 안정된 운용을 보장해 준다. 

 

 

DTO

 

https://maenco.tistory.com/entry/Java-DTO%EC%99%80-VO%EC%9D%98-%EC%B0%A8%EC%9D%B4

 

  • DTO(Data Transfer Object)  계층 간 데이터 교환을 하기 위해 사용하는 객체로, DTO는 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스)이다.
  • 여기서 getter/setter 이외에 다른 로직이 필요 없는 이유를 좀 더 자세히 살펴보자면, 만약 DTO가 데이터 전달 만을 위한 객체라고 한다면 완전히 데이터 '전달' 용도로만 사용하기 때문에 getter/setter로직만이 필요하지 다른 로직이 필요하지 않은 것이다.
  • 유저가 입력한 데이터를 DB에 넣는 과정을 보면,
    • 유저가 자신의 브라우저에서 데이터를 입력하여 form에 있는 데이터를 DTO에 넣어서 전송한다.
    • 해당 DTO를 받은 서버가 DAO를 이용하여 데이터베이스로 데이터를 집어넣는다.

 

VO

  • VO(Value Object) 값 오브젝트로써 값을 위해 쓰인다. (VO는 값 그 자체를 나태는 객체이다.)
  • DTO와 반대로 로직을 포함할 수 있으며, VO의 경우 특정 값 자체를 표현하기 때문에 불변성의 보장을 위해 생성자를 사용하여야 한다.
  • read-Only 특징(사용하는 도중에 변경 불가능하며 오직 읽기만 가능)을 가진다.
  • DTO와 유사하지만 DTO는 setter를 가지고 있어 값이 변할 수 있다.
  • VO는 서로 다른 이름을 갖는 VO 인스턴스라도 모든 속성 값이 같다면 두 인스턴스는 같은 객체인 것이 핵심이다.
  • 반면, DTO는 서로 다른 이름을 갖는 두 DTO인스턴스가 속성 값이 같더라도 두 인스턴스는 다른 객체라는 점에서 차이가 있다.  

https://www.notion.so/Spring-7-d4935026ed68416aa6e079692ce627aa (은솔님조 노션자료 줍줍 ^^)

 


참고 :

 

과제는 이미 제출했지만, 조원분들과 코드리뷰를 하면서 delete 또한 Dto를 사용하는 것이 더 좋은 방법인 것 같아 연습용으로 만들었던 memo프로젝트 파일을 수정해보았다.

 


 

1. DelResponseDto 파일을 생성

 

2. Controller 부분 수정

  • 반환타입을 Map<>방식에서 DelResponseDto로 바꿔주고,
  • return 값도 .getPassword()로 비밀번호 매개값만 넣어 주었다.

 

3. Service 부분

  • Controller와 마찬가지로 반환타입을 새로 만들어준 DelResponseDto로 바꿔주었고, 
  • 아래 result라는 변수명으로 생성자를 하나 만들어주었다.
  • 그리고 if조건문을 통해 해당 id 값에 해당하는 입력된 비밀번호와 DB비밀번호 일치 여부에 따라 .setResult()안에 입력값을 주었다.

 


PostMan에서 확인해 보기

 

1. 먼저 틀린 비밀번호를 넣어서 반환 값을 확인해 보았다.

 

 

2. 다음 일치하는 비밀번호를 넣어주고 반환 값을 확인해 보았다.

 

 

 

의도한대로 반환값이 잘 들어오는 것을 확인 ! 

다른 사람의 코드를 보는게 정말 중요하다는 것을 다시 한번 느낌 ! 

 

 

※ 읽기 전에 !

 

이번 글은 과제를 하면서 작성한 코드를 이해하고자 내멋대로 주석을 달아본 글이기 때문에, 정답이 아닐 수 있으며 ! 정신 건강을 헤칠수도 있습니다 ^__^

 


 1. HanghaememoApplication

 

@EnableJpaAuditing

  • 스프링 부트의 Entry 포인트인 실행 클래스에 @EnableJpaAuditing 어노테이션을 적용하여 JPA Auditing을 활성화 해준다.
  • 생성일자와 수정일자를 자동으로 등록할 수 있는 기능 외에도 EnableJpaAuditing 에는 다양한 기능이 있다.
  • 이 중 auditorAwareRef 는 @CreatedBy, @LastModifiedBy 과 함께 생성한 사람, 수정한 사람을 자동으로 저장해준다.

 

@SpringBootApplication

  • 스프링 부트의 가장 기본적인 설정을 선언해 주는 어노테이션. 해당 어노테이션을 보면 아래와 같은 어노테이션이 다시 선언되어 있다.
  • @Configuration, @EnableAutoConfiguration, @ComponentScan 3가지를 하나의 애노테이션으로 합친 것이다.
  • @ComponentScan은 @component 어노테이션 및 @Service, @Repository, @Controller 등의 어노테이션을 스캔하여 Bean으로 등록해주는 어노테이션이다.
  • @EnableAutoConfiguration은 사전에 정의한 라이브러리들을  Bean으로 등록해 주는 어노테이션이다. 
    사전에 정의한 라이브러리들 모두가 등록되는 것은 아니고 특정 Condition(조건)이 만족될 경우에 Bean으로 등록한다.

2. Entity ( Memo, Timestamped)

entity 에서 실제 DB의 테이블과 매칭될 객체들을 명시해준다.

 

Entity/Memo

 

@Entity 어노테이션으로 명시한 부분
DB 테이블에 들어갈 항목들
Service에서 사용할 생성자를 명시해준다.

 

Entity/Timestamped

 

 

3. Controller

Client와 Service 를 연결하는 역할이라고 할 수 있다. 이 과정에서 반환값을 Dto에 담았다. (Delete 부분 제외)

 

  • @RestController는 @Controller에 @ResponseBody가 추가된 것. 당연하게도 RestController의 주용도는 Json 형태로 객체 데이터를 반환하는 것
  • @RequiredArgsConstructor : 이 어노테이션은 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 준다. 주로 의존성 주입(Dependency Injection) 편의성을 위해서 사용되곤 한다.

 

  • 내가 이해한 바는, 컨트롤러는 클라이언트와 서비스를 연결해 주는 곳이라는 것.
  • 필요한 작업 (메소드)는 서비스클래스에서 처리가 되기 때문에 컨트롤러에서 클라이언트로 부터 받은 requestDto를 서비스에 전달하면 서비스클래스의 메소드가 필요한 작업을 처리하고, 다시 컨트롤러를 통해 클라이언트로 responseDto 형태의 값을 전달한다고 이해하면 되려나 ( ? ) 

 

4. Dto ( MemoRequestDto, MemoResponseDto )

 

Dto는 약간 포장지에 감싸져있는 데이터인 것 같다. 

클라이언트에서 받은 정보를 바로 DB에 저장하고 그걸 다시 클라이언트 쪽으로 전달하게 된다면, 보안상 위험한 부분이 있을 수 있고 나중에 SQL의 join 기능을 사용할 때 문제가 될 수도 있기 때문에, 필요한 데이터가 안전하게 포장되어 있는 것 이라고 이해했다. 아닐 수도 ^_^

 

MemoRequestDto

 

  • 클라이언트에서 입력을 받아 저장되는 데이터들

 

MemoResponseDto

 

  • 클라이언트 쪽에서 요청이 들어왔을 때 꺼내서 보여지는 데이터들. 
  • password는 빠진 것을 볼 수 있다.

 

5. Service

Service는 컨트롤러에서 받아온 데이터 정보를 잘 가공해 DB와 연결된 Repository에 전달 하기도하고, 다시 DB에서 Repository로 보내준 데이터를 가공하여 컨트롤러쪽으로 보내는 역할을 하는 곳이다. 비지니스 로직을 담당하는 곳이기 때문에 메소드들이 모여있는 곳이기도 하다.

 

  • @Service : 해당 클래스가 비즈니스 로직을 담은 Service 클래스임을 명시합니다. @Component 어노테이션을 사용해도 상관 없지만 @Sevice 어노테이션에 @Component 어노테이션의 기능이 포함되어 있고 @Service를 사용함으로써 해당 클래스가 Service의 역할을 하는 것을 명확하게 알 수 있습니다.
  • @RequiredArgsConstructor : 이 어노테이션은 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 줍니다. 주로 의존성 주입(Dependency Injection) 편의성을 위해서 사용되곤 합니다.

 

게시글 POST 메소드 부분

 

  • @Transactional이 붙은 메서드는 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소
  • 데이터베이스를 다룰 때 트랜잭션을 적용하면 데이터 추가, 갱신, 삭제 등으로 이루어진 작업을 처리하던 중 오류가 발생했을 때 모든 작업들을 원상태로 되돌릴 수 있다. 이 기능을 하는 어노테이션이 바로 @Transactional

 

 

게시글 전체조회 GET 메소드 부분

 

 

 

게시글 선택 조회 GET 메소드 부분

 

 

 

게시글 수정 PUT 메소드 부분

 

 

 

게시글 삭제 DELETE 메소드 부분

 

 

 

6. Repository

Repository는 Entity에 의해 생성된 DB에 접근하는 메서드를 사용하기위한 interface를 말한다. 저장소와 직접적으로 연결되어 있어 Service에서 필요한 데이터를 DB에서 Entity 클래스로 받아와 다시 Dto로 전달하는 곳이다. 

 

  • 아직 쿼리형식은 공부하지 않았는데, 강의에서 나온대로 쿼리형식으로 지정이 되어있다.
  • 인터페이스란 무엇일까 ? 
  • 인터페이스란 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면이다. 컴퓨팅에서 컴퓨터 시스템끼리 정보를 교환하는 공유 경계이다.

 


일단 이번 과제에 작성된 코드를 쭉 한번 살펴봤다. 

 

아직 모르는 개념도 많고 어노테이션도 많고 그 외 이해가 되지 않는 부분이 더 많지만, 대략적인 흐름을 알 수 있었던 것 같다.

개발자마다 순서가 조금씩 다르긴 한데, 보통 entity를 먼저 만들고, 그다음 controller를 만들고, 그다음 Repository, 다음  Service와 Dto를 만드는 것 같다. 기술매니저님이 서비스의 규모가 커질 수록 Dto의 수가 늘어나는 것은 당연한 일이기 때문에, Dto를 만드는 것을 불편해 하지 말라고 하셨다. 이번엔 간단한 프로젝트라 두개의 Dto로 끝나긴 했지만, 앞으로 실전 프로젝트를 위해선 원하는 형식의 Dto를 설계하는 법도 공부해야 할 것 같다. 

+ Recent posts