Spring 간단 정리 - Kim-Taesu/study GitHub Wiki

  • www
    • 인터넷에 연결된 컴퓨터를 통해 사람들이 정보를 공유할 수 있는 전 세계적인 정보 공간을 말한다.
    • 간단히 웹(Web)이라 부르는 경우가 많다.
  • 인터넷
    • 컴퓨터로 연결하여 TCP/IP 라는 통신 프로토콜을 이용해 정보를 주고받는 컴퓨터 네트워크이다.

  • 인터넷 프로토콜 스위트 (TCP/IP 프로토콜)

    • 인터넷에서 컴퓨터들이 서로 정보를 주고받는 데 쓰이는 통신규약(프로토콜)의 모음이다.
    • 인터넷 프로토콜 슈트 중 TCP와 IP가 가장 많이 쓰이기 때문에 TCP/IP 프로토콜 슈트라고도 불린다.
    1. 네트워크 인터페이스 계층
      • PPP(점대점 프로토콜)
        • 네트워크 분야에서 두 통신 노드 간의 직접적인 연결을 위해 일반적으로 사용되는 데이터 링크 프로토콜이다.
        • 점대점 프로토콜은 인증, 암호화[1]를 통한 전송 및 데이터 압축 기능을 제공한다.
      • 이더넷
        • 컴퓨터 네트워크 기술의 하나로, 일반적으로 LAN, MAN 및 WAN에서 가장 많이 활용되는 기술 규격이다.
    2. ARP
      • ARP(주소 결정 프로토콜)
        • 네트워크 상에서 IP 주소를 물리적 네트워크 주소로 대응(bind)시키기 위해 사용되는 프로토콜이다.
    3. 인터넷 계층
      • 네트워크 경계를 거쳐 호스트로부터 기원한 네트워크 패킷을 필요하면 IP 주소로 지정된 목적지 호스트로 전송하기 위해 사용된다.
      • IPv4, IPv6, ICMP
      • ICMP : 네트워크 컴퓨터 위에서 돌아가는 운영체제에서 오류 메시지(Requested service is not available 등)를 전송받는 데 주로 쓰이며 인터넷 프로토콜의 주요 구성원 중 하나로 인터넷 프로토콜에 의존하여 작업을 수행한다.
    4. 전송 계층
      • 계층 구조의 네트워크 구성요소와 프로토콜 내에서 송신자와 수신자를 연결하는 통신 서비스를 제공한다.
      • 연결 지향 데이터 스트림 지원, 신뢰성, 흐름 제어, 그리고 다중화와 같은 편리한 서비스를 제공한다.
      • TCP
        • 인터넷 프로토콜 스위트(IP)의 핵심 프로토콜 중 하나로, IP와 함께 TCP/IP라는 명칭으로도 널리 불린다.
        • TCP는 근거리 통신망이나 인트라넷, 인터넷에 연결된 컴퓨터에서 실행되는 프로그램 간에 일련의 옥텟을 안정적으로, 순서대로, 에러없이 교환할 수 있게 한다.
      • UDP
        • 인터넷 프로토콜 스위트의 주요 프로토콜 가운데 하나이다.
        • UDP의 전송 방식은 너무 단순해서 서비스의 신뢰성이 낮고, 데이터그램 도착 순서가 바뀌거나, 중복되거나, 심지어는 통보 없이 누락시키기도 한다.
        • UDP는 일반적으로 오류의 검사와 수정이 필요 없는 애플리케이션에서 수행할 것으로 가정한다.
    5. 응용 계층
      • 인터넷 프로토콜(IP) 컴퓨터 네트워크를 통하는 프로세스 간 통신 접속을 위해 설계되어 통신 프로토콜과 방식을 위해 보유된 추상 계층이다.
      • DNS, TLS/SSL, FTP, HTTP, IMAP, IRC, SMTP, SSH, 텔넷, 비트토렌트, RTP
      • HTTP
        • WWW 상에서 정보를 주고받을 수 있는 프로토콜이다.
        • 주로 HTML 문서를 주고받는 데에 쓰인다.
        • TCP와 UDP를 사용하며, 80번 포트를 사용한다.
        • 클라이언트와 서버 사이에 이루어지는 요청/응답(request/response) 프로토콜
        • 클라이언트인 웹 브라우저가 HTTP를 통하여 서버로부터 웹페이지나 정보를 요청하면, 서버는 이 요청에 응답하여 필요한 정보를 해당 사용자에게 전달하게 된다. 이 정보가 모니터와 같은 출력 장치를 통해 사용자에게 나타나는 것이다.
      • DHCP
        • 호스트 IP 구성 관리를 단순화하는 IP 표준이다.
        • DHCP 서버를 사용하여 IP 주소 및 관련된 기타 구성 세부 정보를 네트워크의 DHCP 사용 클라이언트에게 동적으로 할당하는 방법을 제공
      • HTTPS
        • 월드 와이드 웹 통신 프로토콜인 HTTP의 보안이 강화된 버전이다.
        • HTTPS는 소켓 통신에서 일반 텍스트를 이용하는 대신에, SSL이나 TLS 프로토콜을 통해 세션 데이터를 암호화한다.
          • SSL : 인터넷 보안 프로토콜
          • TLS : 컴퓨터 네트워크에 통신 보안을 제공하기 위해 설계된 암호 규약
          • 데이터의 적절한 보호를 보장.
        • HTTPS의 기본 TCP/IP 포트는 443이다.
      • SSH
        • 네트워크 상의 다른 컴퓨터에 로그인하거나 원격 시스템에서 명령을 실행하고 다른 시스템으로 파일을 복사할 수 있도록 해 주는 응용 프로그램 또는 그 프로토콜
        • SSH는 암호화 기법을 사용하기 때문에, 통신이 노출된다고 하더라도 이해할 수 없는 암호화된 문자로 보인다.
  • 패킷 교환 방식

    • TCP/IP 환경에서 데이터는 패킷 단위로 교환
    • 축적 교환 방식 사용 : 구간 별로 중계 루트가 변하는 형태로 상대방에게 전송
      • 장점 : 전송 대역을 효율적으로 이용할 수 있다, 패킷 단위로 데이터의 흐름이 생기고, 패킷에 웃너 순위를 부여할 수 있다.
  • IP

    • 인터넷 상에서 식별할 수 있는 주소
    • 호스트간의 통신만 담당
    • IP 정보는 패킷 혹은 데이터그램이라고 하는 덩어리로 나뉘어 전송
    • 특징
      • 비신뢰성, 비연결성
        • 흐름에 관여하지 않기 때문에 보낸 정보가 제대로 갔는지 보장하지 않는다.
        • 패킷 전송과 정확한 순서를 보장하려면 TCP 프로토콜을 사용해야 한다.
  • 도메인 주소

    • IP 주소를 그대로 사용하기에 어려움이 있어 IP 주소를 문자로 표현

  • 웹서버

    • 클라이언트가 서버에 페이지 요청을 하면 요청을 받아 정적 컨텐츠(.html, .png, .css등)를 제공하는 서버
  • 웹 서버 작동 원리

    image

    ①② 사용자가 웹 브라우저를 통해 찾고 싶은 웹 페이지의 URL 주소를 입력함.

    ③ 사용자가 입력한 URL 주소 중에서 도메인 네임(domain name) 부분을 DNS 서버에서 검색함.

    ④ DNS 서버에서 해당 도메인 네임에 해당하는 IP 주소를 찾아 사용자가 입력한 URL 정보와 함께 전달함.

    ⑤⑥ 웹 페이지 URL 정보와 전달받은 IP 주소는 HTTP 프로토콜을 사용하여 HTTP 요청 메시지를 생성함.

    이렇게 생성된 HTTP 요청 메시지는 TCP 프로토콜을 사용하여 인터넷을 거쳐 해당 IP 주소의 컴퓨터로 전송됨.

    ⑦ 이렇게 도착한 HTTP 요청 메시지는 HTTP 프로토콜을 사용하여 웹 페이지 URL 정보로 변환됨.

    ⑧ 웹 서버는 도착한 웹 페이지 URL 정보에 해당하는 데이터를 검색함.

    ⑨⑩ 검색된 웹 페이지 데이터는 또 다시 HTTP 프로토콜을 사용하여 HTTP 응답 메시지를 생성함.

    이렇게 생성된 HTTP 응답 메시지는 TCP 프로토콜을 사용하여 인터넷을 거쳐 원래 컴퓨터로 전송됨.

    ⑪ 도착한 HTTP 응답 메시지는 HTTP 프로토콜을 사용하여 웹 페이지 데이터로 변환됨.

    ⑫ 변환된 웹 페이지 데이터는 웹 브라우저에 의해 출력되어 사용자가 볼 수 있게 됨.


  • HTML
    • 웹 페이지를 위한 지배적인 마크업 언어다.
    • HTML은 제목, 단락, 목록 등과 같은 본문을 위한 구조적 의미를 나타내는 것뿐만 아니라 링크, 인용과 그 밖의 항목으로 구조적 문서를 만들 수 있는 방법을 제공한다.
    • 이미지와 객체를 내장하고 대화형 양식을 생성하는 데 사용될 수 있다.

  • WAS

    • 동적컨텐츠를 제공하기 위해 만들어진 애플리케이션 서버
  • WAS와 웹 서버 차이

    • 동적 컨텐츠 처리를 수행 가능한가 아닌가.
    • WAS는 정적,동적 처리 둘다 가능하지만 정적처리를 WAS가 하게되면 부하가 많이 걸려서 좋지 않음

  • Container
    • 보통 객체 인스턴스의 생명주기를 관리하며, 생성된 인스턴스들에게 추가적인 기능을 제공하도록하는 것

  • Servlet
    • Container가 이해할 수 있게 구성된 순수 자바코드로만 이루어짐
    • 요청 당 쓰레드 (만들거나, 풀에서 가져다가 ) 사용
    • java에서 html 코드를 작성
    • 서블릿은 MVC 접근 방식에서 컨트롤러 역할을 한다.
    • Servlet 수정 시 server를 recompile 하거나 restart 해야 한다.
    • Business logic과 Presentation logic을 분리할 수 없다.
  • JSP
    • html 기반에 JAVA 코드를 블록화하여 삽입한 것으로 Servlet을 좀 더 쉽게 접근할 수 있도록 만들어 진 것
    • JSP를 Servlet(Java 코드)로 변환하고 컴파일 하기 때문에 Servlet 보다는 느리다.
    • Business logic과 Presentation logic을 분리할 수 있다.
    • Beans라고 하는 자바 컴포넌트 사용 가능
  • JSP 동작 원리
    1. 웹 브라우저에서 http://blog.naver.com:8080/index.jsp를 입력합니다.
    2. 웹 브라우저는 http://blog.naver.com라는 도메인을 가진 ip 주소를 DNS서버로부터 받습니다.
    3. IP주소의 해당 서버 8080번 포트로 접속을 시도합니다.
    4. http://blog.naver.com의 웹 서버는 8080번 포트로 접속을 기다리다 접속 요청이 들어오면 받습니다.
    5. 웹 서버는 내용을 분석하고, 서블릿 컨테이너에게 요청을 넘깁니다.
    6. 서블릿 컨테이너는 jsp파일에 해당하는 서블릿이 있는지 확인하고, 없을 경우 jsp파일을 서블릿으로 컴파일 합니다.
    7. 컴파일된 jsp는 서블릿으로 변환되어 컨테이너에 적재됩니다.
    8. 서블릿 내용 중 데이터베이스 처리 부분이 있으면, 데이터베이스에서 데이터를 가져옵니다.
    9. 화면에 보일 내용을 정리해서 html문서형태로 클라이언트에 전송합니다.
    10. 웹 브라우저는 웹 서버에서 보낸 텍스트 내용 중 html태그를 분석해서 적절히 변환하여 화면을 구성합니다.

  • Servlet Life Cycle
    1. servlet 생성시 init()을 호출한다.
    2. Client로부터 발생한 모든 요청은 service()를 통해 다루게 된다.
    3. Servlet이 서비스를 마치게 되면 destroy()를 통해 종료되고, GC에 의해 메모리 정리가 된다.

  • Servlet Container

    • servlet의 생성부터 소멸까지의 일련의 과정(Lifer Cycle)을 관리한다.

      • 대표적인 Servlet Container가 Tomcat 이다.

      https://s3-us-west-2.amazonaws.com/secure.notion-static.com/35324896-57b8-44a9-be2e-239cbfcb3f9d/Untitled.png

  • Servlet Config

    • Servlet이 초기화 될 때 생성되는 객체
    • ServletConfig가 공유한는 정보는 특정 Servlet에 대한 것이다.
    • Servlet 초기화 단계에서 ServletContainer가 생성하고 Servlet에게 전달하는 초기 파라미터 또는 설정 정보를 담고 있는 체이다.
    • ServletConfig는 특정 Servlet을 위한 것이다.
      • Servlet 정보를 web.xml에 저장하고 객체를 사용하여 검색하야 한다.
  • Servlet Context

    • Servlet이 초기화 될 때 생성되는 객체
    • 웹 애플리케이션의 모든 서블릿에서 사용 가능하다.
    • ServletContext는 ServletContainer가 전체 응용 프로그램과 초기 매개 변수 또는 구성 정보를 공유하기 위해 만든 객체이다.

    https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4c9be3b9-b2bf-4873-8a11-605dc2992461/Untitled.png

  • ServletContextListener

    • 웹 어플리케이션의 생명주기를 감시하는 Listener
    • XML형태의 Deploy Descriptor에서는 param-value가 String으로만 가능한 단점.
      • param-value에 Object를 넣기 위해 등장.
    • 웹 어플리케이션의 시작과 종료시 호출
      • contextInitialized()
      • contextDestroyed()
  • Root Application Context

    • 전체 계층구조에서 최상단에 위치한 컨텍스트
    • 서로 다른 서블릿 컨텍스트에서 공유해야하는 Bean들을 등록해놓고 사용할 수 있다.
    • 웹 어플리케이션 전체에 적용가능한 DB 연결, 로깅기능등에 이용
    • Servlet Context에 등록된 Bean 이용 불가능 하다
    • Servlet Context와 동일한 Bean이 있을 경우 Servlet Context Bean이 우선된다
    • 하나의 컨텍스트에 정의된 AOP 설정은 다른 컨텍스트의 빈에는 영향을 미치지 않는다.

  • Bean
    • 스프링 IoC 컨테이너가 관리하는 객체
    • 장점
      • 의존성 관리
      • 스코프
        • 싱글톤 : 하나의 객체를 반환
        • 프로토타입 : 매번 다른 객체 반환
  • IoC
    • 인스턴스의 생성과 소멸의 제어권이 컨테이너로 넘어가게 되어 컨테이너가 인스턴스 생성과 소멸을 관리
    • 스프링 컨테이너가 애플리케이션의 흐름을 제어하는 권한이 없다면 @Autowired를 통한 의존성 주입(DI)을 할 수 없다.
  • 스프링 IoC 컨테이너
    • ApplicationContext
      • 스프링에서 IoC Container 역할
        • IoC Container도 bean이다.
      • 빈 설정 소스 로 부터 빈 정의 를 읽어들이고, 빈을 구성하고 제공 한다.
      • BeanFactory 인터페이스를 구현
        • DI의 기본사항을 제공하는 가장 단순한 컨테이너
        • 팩토리 디자인 패턴을 구현
        • Bean을 생성하고 분배하는 책임을 지는 클래스
        • Bean이 필요할 때까지 인스턴스화 하지 않는다.
      • 추가적인 ApplicationContext만의 기능
        • 메시지 소스 처리 기능 (i18n)
        • 이벤트 발행 기능
        • 리소스 로딩 기능
  • DI
    • Spring 프레임워크에서 지원하는 IoC의 형태이다.
    • IoC로 특정 객체에 필요한 객체를 외부에서 결정해서 연결 시키는 것을 의미한다.
    • DI의 세가지 방법
      • Contructor Injection : 생성자 삽입
      • Method(Setter) Injection : 메소드 매개 변수 삽입
      • Field Injection : 멤버 변수 삽입

  • @ComponentScan
    • 특정 패키지 이하의 모든 클래스 중에 @Component 애노테이션을 사용한 클래스를 빈으로 자동으로 등록 해 줌.

  • 스프링 프레임워크
    • 자바 엔터프라이즈 개발을 편하게 해주는 경량급 오픈소스 애플리케이션 프레임워크
    • 목표 : POJO 기반의 Enterprise Application 개발을 쉽고 편하게 할 수 있도록 한다.
    • Spring이 Java Application을 개발하는데 필요한 하부구조를 처리하기 때문에 개발자는 Application 개발에 집중할 수 있다.
    • 특징
      • 경량 프레임워크 : 각각의 객체 생성, 소멸과 같은 라이프 사이클을 관리하며 스프링으로부터 필요한 객체를 얻어올 수 있다.
      • IoC : 이라는 기술을 통해 애플리케이션의 느슨한 결합을 도모
      • AOP
      • 애플리케이션 객체의 생명주기와 설정을 포함하고 관리한다는 점에서 일종의 컨테이너(Container)라고 할 수 있음.
      • 트랜잭션 관리
      • 간단한 컴포넌트로 복잡한 애플리케이션을 구성하고 설정할 수 있음.

  • 스프링 MVC
    • Spring MVC 는 MVC 패턴 기반의 웹 프레임워크이다.
    • MVC 는 Model-View-Controller의 약자로 ModelViewController 로 구성
    • MVC 패턴의 장점
      • 동시 다발적(Simultaneous) 개발 : 백엔드 개발자와 프론트엔드 개발자가 독립적으로 개발을 진행할 수 있다.
      • 높은 결합도 : 논리적으로 관련있는 기능을 하나의 컨트롤러로 묶거나, 특정 모델과 관련있는 뷰를 그룹화 할 수 있다.
      • 낮은 의존도 : 뷰, 모델, 컨트롤러는 각각 독립적이다.
      • 개발 용이성 : 책임이 구분되어 있어 코드 수정하는 것이 편하다.
      • 한 모델에 대한 여러 형태의 뷰를 가질 수 있다.
    • MVC 패턴의 단점
      • 코드 네비게이션 복잡함
      • 코드 일관성 유지에 노력이 필요함
      • 높은 학습 곡선

  • 스프링 MVC 구성요소
    • Model
      • 도메인 객체 또는 DTO로 화면에 전달할 또는 화면에서 전달 받은 데이터를 담고 있는 객체
    • View
      • 데이터를 보여주는 역할.
      • 다양한 형태로 보여줄 수 있다
    • Controller
      • 사용자 입력을 받아 모델 객체의 데이터를 변경하거나, 모델 객체를 뷰에 전달하는 역할.

  • 서블릿 리스너
    • 웹 애플리케이션에서 발생하는 주요 이벤트를 감지하고 각 이벤트에 특별한 작업이 필요한 경우에 사용할 수 있다.
      • 서블릿 컨텍스트 수준의 이벤트
      • 세션 수준의 이벤트
    • Servlet Container 위에 있는 개념
  • 서블릿 필터
    • 들어온 요청을 서블릿으로 보내고, 또 서블릿이 작성한 응답을 클라이언트로 보내기 전에 특별한 처리가 필요한 경우에 사용할 수 있다.

  • ServletContextListener
    • Context의 라이프사이클을 감시
    • 웹애플리케이션의 시작과 종료시 다음의 메소드가 자동으로 호출
  • ContextLoaderListener
    • WenApplicationContext를 만들어 준다.
      • Root WebApplicationContext 를 생성한다.
    • WenApplicationContext를 ServletContext 라이프사이클에 따라 등록하고 소멸시켜준다.
    • 서블릿에서 IoC 컨테이너를 ServletContext를 통해 꺼내 사용할 수 있다.
  • DispatcherServlet
    • Front Controller의 역할
    • Servlet Context에 등록되어 있는 ApplicationContext(Root WebApplicationContext)를 상속하는 ApplicationContext(Servlet WebApplicationContext)를 생성
    • 상속하는 이유 : 다른 Servlet에서 Root WebApplicationContext를 사용할 수 있기 때문
    • Root WebApplicationContext
      • 웹과 관련된 Bean들은 등록되지 않는다.
      • Services, Repositories
    • Servlet WebApplicationContext
      • 웹과 관련된 Bean이 등록
      • Controllers, ViewResolver, HandlerMapping
  • WebApplicationContext
    • ApplicationContext를 확장한 WebApplicationContext 인터페이스의 구현체
    • ApplicationContext에 getServletContext() 메서드가 추가된 인터페이스입니다.
      • 이 메서드를 호출하면 서블릿 컨텍스트를 반환
  • ApplicationContext
    • 스프링이 관리하는 빈들이 담겨 있는 컨테이너

  • Spring AOP
    • OOP를 보완하는 수단으로, 흩어진 Aspect를 모듈화 할 수 있는 프로그래밍 기법.
    • 용어
      • Concerns
        • 여러 메소드나 클래스에서 사용하는 비슷한 코드
      • Aspect
        • Crosscutting Concerns들을 모듈화한 것
      • Target
        • Aspect의 Advice를 적용할 대상
      • Advice
        • 해야할 일들
      • Join point
        • 특정 실행 시점에 Advice를 끼워 넣을 수 있는 지점
      • Pointcut
        • 어디에 적용해야 하는지
    • 적용 방법
      • 컴파일
      • 로드 타임
      • 런타임

  • MVC 1

    • JSP 페이지 안에서 로직 처리를 위해 자바 코드가 함께 사용된다.
    • 요청이 오면, 직접 자바빈이나 클래슬르 이용해 작업을 처리하고, 이를 클라이언트에 출력
    • 구조가 단순하지만 JSP 내에서 html 코드와 자바 코드가 같이 사용되면서 복잡해지고 유지보수가 어려운 단점이 있습니다.
  • MVC 2

    • 모든 처리를 MVC 1 방식처럼 JSP에서만 담당하지 않고 서블릿을 만들어 역할 분담을 하는 패턴
      • 요청 결과를 출력해주는 뷰만 JSP가 담당하고, 흐름을 제어해주고 비즈니스 로직에 해당하는 컨트롤러의 역할을 서블릿이 담당
    • 유지보수가 용이해지는 장점과 초기 학습의 어려움과 구조가 복잡해지는 단점이 있다.

  • Filter vs Interceptor
    • 차이점
      • 실행되는 시점
    • Filter는 웹 애플리케이션에 등록을 하고 Interceptor는 스프링의 context에 등록한다.
    • Filter는 Dispatcher 앞단에 존재하고 Interceptor는 Controller의 앞단에 존재합니다.

  • DispatcherServlet 동작 순서
    1. 클라이언트의 요청을 분석한다. (로케일, 테마, 멀티파트 등)
    2. 핸들러 맵핑에게 위임하여 요청을 처리할 핸들러를 찾는다.
    3. 등록되어 있는 핸들러 어댑터 중에 해당 핸들러를 실행할 수 있는 핸들러 어댑터를 찾는다.
    4. 핸들러 어댑터에서 자바 리플렉션을 사용하여 컨트롤러를 실행하고 응답을 처리한다.
      • 핸들러의 리턴값을 보고 어떻게 처리할지 판단한다.
      • 뷰 이름에 해당하는 뷰를 찾아서 모델 데이터를 랜더링한다.
      • @ResponseEntity가 있다면 Converter를 사용해서 응답 본문을 만들고.
      • (부가적으로) 예외가 발생했다면, 예외 처리 핸들러에 요청 처리를 위임한다.
    5. 최종적으로 응답을 보낸다.

  • Spring MVC 구성요소
    • Dispatcherservlet
      • 클라이언트의 요청을 전달 받아 컨트롤러에게 요청을 전달하고 컨트롤러가 리턴할 결과 값을 view에게 전달하여 알맞은 응답을 하도록 하는 것
      • 클라이언트의 요청을 중앙에서 처리하는 스프링 MVC의 핵심 구성 요소
      • web.xml에 한개 이상의 DispatcherServlet을 설정한다.
    • HandlerMapping
      • 클라이언트의 요청 URL을 어떤 컨트롤러가 처리할지를 결정
    • Controller
      • 클라이언트의 요청을 처리한 뒤 그 결과를 DispatcherServlet에게 알려준다.
    • ModelAndView
      • 컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담아 놓는 세트
    • ViewResolver
      • 컨트롤러의 처리 결과를 생성할 뷰를 결정
    • View
      • 컨트롤러에서 리턴 받은 처리 결과를 화면에 출력한다.

  • REST(Representational State Transfer)
    • 네트워크 상에서 클라이언트와 서버 사이의 통신 방법 중 하나로 자원을 이름으로 구분하여 정보를 주고 받는 것
    • HTTP URI를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.
    • 구성 요소
      • 자원 : URI
      • 행위 : HTTP Method
      • 표현
  • RESTful
    • RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어이다.
    • ‘REST API’를 제공하는 웹 서비스를 ‘RESTful’하다고 할 수 있다.

  • API
    • 프로그램을 쉽게 제작할 수 있게 미리 만들어 놓은 것들의 모음

  • Springboot의 자동설정
    • application main클래스에 설정된 @SpringBootApplication@EnableAutoConfiguration 때문
    • Bean을 등록하는 방법
      • 1단계 : @ComponentScan으로 스캔하여 Bean을 등록
        • @Component 를 가진 클래스를 스캔해서 Bean으로 등록
      • 2단계 : @EnableAutoConfiguration 로 추가적인 Bean 등록
        • spring.factories 파일에 정의된 설정파일로 Bean 등록
        • 조건에 따라 Bean의 등록 여부 설정

  • 스프링 Test
    • @SpringBootTest
      • @RunWith(SpringRunner.class)랑 같이 써야 함.
      • 빈 설정 파일은 설정을 안해주나? 알아서 찾습니다. (@SpringBootApplication)
      • webEnvironment
        • MOCK: mock servlet environment. 내장 톰캣 구동 안 함.
        • RANDON_PORT, DEFINED_PORT: 내장 톰캣 사용 함.
        • NONE: 서블릿 환경 제공 안 함.

슬라이스 테스트

  • Spring HATEOAS
    • Hypermedia As The Engine Of Application State
    • 서버는 현재 리소스와 연관된 링크 정보 를 클라이언트에게 제공하고 클라이언트는 연관된 링크 정보 를 바탕으로 리소스에 접근한다.

  • SOP과 CORS
    • Single-Origin Policy
      • 같은 Origin에만 요청을 보낼 수 있다.
    • Cross-Origin Resource Sharing
      • Single-Origin Policy를 우회하기 위한 표준 기술
      • 서로 다른 Origin에서 요청을 보낼 수 있다.
    • Origin?
      • URI 스키마 (http, https)
      • hostname (localhost)
      • 포트 (8080, 18080)
    • 스프링 MVC @CrossOrigin
      • @Controller@RequestMapping에 추가하거나
      • WebMvcConfigurer 사용해서 글로벌 설정

  • 스프링 트랜잭션
    • 트랜잭션의 정의
      • 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.
    • 트랜잭션 성질 (ACID)
      • 원자성 : 한 트랜잭션 내에서 실행한 작업들은 하나로 간주(성공 or 실패)
      • 일관성 : 트랜잭션은 일관성 있는 데이터베이스 상태를 유지
      • 격리성 : 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리
      • 지속성 : 트랜잭션을 성공적으로 마치면 결과가 항상 저장
    • 스프링에서는 트랜잭션 처리를 지원하는데 그중 어노테이션 방식으로 @Transactional을 선언하여 사용하는 방법이 일반적이며, 선언적 트랜잭션이라 부른다.
      • 클래스, 메서드위에 @Transactional 이 추가되면, 이 클래스에 트랜잭션 기능이 적용된 프록시 객체가 생성된다.
      • 이 프록시 객체는 @Transactional이 포함된 메소드가 호출 될 경우, PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 정상 여부에 따라 Commit 또는 Rollback 한다

  • 연관관계 매핑
    • 엔티티들이 서로 어떤 연관관계를 맺는지 파악하는 것
    • 객체의 참조와 테이블의 외래키를 매핑하는 것
    • 방향
      • 단방향 : 두 엔티티가 관계를 맺을 때, 한 쪽의 엔티티만 참조하고 있는 것을 의미
      • 양방향 : 두 엔티티가 관계를 맺을 때, 양 쪽이 서로 참조하고 있는 것을 의미
    • 다중성
      • 어떤 엔티티를 중심으로 상대 엔티티를 바라 보느냐에 따라 다중성이 다르다.
      • 엔티티 자신을 기준으로 다중성을 결정
      • Many To One (@ManyToOne)
        • @JoinColumn 외래 키를 매핑 할 때 사용 (필드명 + "_" + 참조 테이블 기본 키 컬럼)
      • One To Many (@ManyToOne)
      • One To One (@ManyToOne)
        • mappedBy 옵션을 사용하여 주인 여부를 알 수 있다.
      • Many To Many (@ManyToOne)

  • ORM
    • 애플리케이션의 클래스와 SQL 데이터베이스의 테이블 사이의 맵핑 정보를 기술한 메타데이터 를 사용하여, 자바 애플리케이션의 객체를 SQL 데이터베이스의 테이블에 자동으로 (또 깨끗하게) 영속화 해주는 기술입니다
  • JPA
    • JPA는 Java Persistence API의 약자로, 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
    • Hibernate는 JPA의 구현체이다

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dec581bd-1725-4389-aeca-3545a594226d/Untitled.png

  • JPA 프로그래밍 : Entity 매핑

    • @Entity : 객체 세상에서 부르는 이름
    • @Table : 릴레이션, @Entity의 이름이 기본 값
    • @Id : 엔티티의 주키를 맵핑할 때 사용
    • @GeneratedValue : 주키의 생성 방법을 맵핑하는 애노테이션
      • 기본키 자동생성 방법
        • IDENTITY
          • 기본 키 생성을 데이터베이스에 위임하는 방법 (데이터베이스에 의존적)
          • 주로 MySQL, PostgresSQL, SQL Server, DB2에서 사용합니다.
        • SEQUENCE
          • 데이터베이스 시퀀스를 사용해서 기본 키를 할당하는 방법 (데이터베이스에 의존적)
          • 주로 시퀀스를 지원하는 Oracle, PostgresSQL, DB2, H2에서 사용합니다.
          • @SequenceGenerator를 사용하여 시퀀스 생성기를 등록하고, 실제 데이터베이스의 생성될 시퀀스이름을 지정해줘야 합니다.
        • TABLE
          • 키 생성 테이블을 사용하는 방법
          • 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만드는 방법입니다.
          • 테이블을 사용하므로, 데이터베이스 벤더에 상관없이 모든 데이터베이스에 적용이 가능합니다.
        • AUTO(default)
          • 데이터베이스 벤더에 의존하지 않고, 데이터베이스는 기본키를 할당하는 방법
          • 데이터베이스에 따라서 IDENTITY, SEQUENCE, TABLE 방법 중 하나를 자동으로 선택해주는 방법입니다.
          • 예를들어, Oracle일 경우 SEQUENCE를 자동으로 선택해서 사용합니다. 따라서, 데이터베이스를 변경해도 코드를 수정할 필요가 없습니다.
    • @Column : 컬럼 관련 속성 지정
      • unique, nullable, length, columnDefinition, ...
    • @Temporal : 날짜 관련 변수에 사용
    • @Transient : 컬럼으로 맵핑하고 싶지 않은 멤버 변수에 사용
  • Value 타입 맵핑

    • 종류
      • 기본 타입 (String, Date, Boolean, ...)
      • Composite Value 타입
      • Collection Value 타입
        • 기본 타입의 Collection
        • Composite 타입의 Collection
    • Composite Value 타입 맵핑
      • @Embeddable
      • @Embedded
      • @AttributeOverrides
      • @AttributeOverride
    @Embeddable
    public class Address {
    	private String street;
    	private String city;
    	private String state;
    	private String zipCode;
    }
    
    ---
    
    @Entity
    public class Account {
    	@Id @GeneratedValue
    	Long Id;
    	...
    	@Embedded
    	@AttributeOverrides({
    		@AttributeOverride(name = "street",	column = @Column(name = "home_street"))
    	})
    	private Address address;
    
  • JPA Cascade

    • 엔티티의 상태 변화를 전파 시키는 옵션
    • 엔티티의 상태
      • Transient : JPA가 모르는 상태
      • Persistent : JPA가 관리중인 상태
      • Detached : JPA가 더이상 관리하지 않는 상태
      • Removed : JPA가 관리하긴 하지만 삭제하기로 한 상태

    https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b9ec261f-a0f0-46bb-9af2-a52afa574b67/Untitled.png

  • JPA Fetch

    • 연관 관계의 엔티티를 어떻게 가져올 것인지 설정
      • Eager
      • Lazy
    • @OneToMany의 기본값은 Lazy
    • @ManyToOne의 기본값은 Eager
  • 영속성 컨텍스트의 특징

    • 영속성 컨텍스트와 식별자 값영속성 컨텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본 키와 매핑한 값)으로 구분한다.따라서 영속 상태는 식별자 값이 반드시 있어야 한다!
    • 영속성 컨텍스트와 데이터베이스 저장이전 포스팅에서 말했듯이 JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영한다. 이를 플러시(flush) 라고 한다.
    • 영속성 컨텍스트가 엔티티를 관리하면 얻게되는 장점
      • 1차 캐시
      • 동일성(identity) 보장
      • 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
      • 변경 감지(Dirty Checking)
      • 지연 로딩(Lazy Loading)
  • 1차 캐시

    • 영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 1차 캐시라 한다.
    • 영속 상태의 엔티티는 모두 이곳에 저장된다.
    • 쉽게 말해 영속성 컨텍스트 내부에 Map이 하나 있는데 (1차 캐시), 키는 @Id로 매핑한 식별자고 값은 엔티티 인스턴스다.
    • 장점
      • 엔티티를 조회하는 find 메서드가 실행되면
      1. 1차 캐시에서 식별자 값으로 엔티티를 찾는다.
      2. 만약 찾는 엔티티가 1차 캐시에 있으면 데이터베이스를 조회하지 않고 메모리에 있는 1차 캐시에서 엔티티를 조회
      3. 1차 캐시에 찾는 엔티티가 없으면 데이터베이스에서 조회
  • 변경 감지(dirty checking)

    • 엔티티의 변경사항을 데이터베이스에 자동으로 반영하는 기능
    • JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해두는데 이것을 스냅샷 이라 한다
    • 과정
      1. 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(flush())가 호출된다.
      2. 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
      3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
      4. 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
      5. 데이터베이스 트랜잭션을 커밋한다.
    • 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용된다
      • 비영속, 준영속처럼 영속성 컨텍스트의 관리를 받지 못하는 엔티티는 값을 변경해도 데이터베이스에 반영되지 않는다
    • JPA의 기본 전략은 엔티티의 모든 필드를 업데이트 한다.
      • 단점 : 모든 필드를 업데이트하면 데이터베이스에 보내는 데이터 전송량이 증가
      • 장점
        • 모든 필드를 사용하면 수정 쿼리가 항상 같다. 따라서 애플리케이션 로딩 시점에 수정 쿼리를 미리 생성해두고 재사용할 수 있다.
        • 데이터베이스에 동일한 쿼리를 보내면 데이터베이스는 이전에 한 번 파싱된 쿼리를 재사용할 수 있다.
  • 엔티티 삭제

    • remove()에 삭제 대상 엔티티를 넘겨주면 엔티티를 삭제한다.
    • 물론 엔티티를 즉시 삭제하는 것이 아니라, 엔티티 등록과 비슷하게 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록한다.
    • 이후 트랜잭션을 커밋해서 플러시를 호출하면 실제 데이터베이스에 삭제 쿼리를 전달
  • 플러시

    • 플러시(flush())는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
    • 플러시를 실행하면 다음과 같은 일이 일어난다.
      1. 변경 감지가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾는다.
      2. 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
      3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.(등록, 수정, 삭제 쿼리)
  • 영속성 컨텍스트를 플러시 하는 3가지 방법

    • em.flush() - 직접 호출
      • 엔티티 매니저의 flush() 메서드를 직접 호출해서 영속성 컨텍스트를 강제로 플러시한다.
    • 트랜잭션 커밋 - 플러시 자동 호출
      • 데이터베이스에 변경 내용을 SQL로 전달하지 않고 트랜잭션만 커밋하면 어떤 데이터도 데이터베이스에 반영되지 않는다.
      • 따라서 트랜잭션을 커밋하기 전에 꼭 플러시를 호출해서 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영해야 한다.
      • JPA는 이런 문제를 예방하기 위해 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다.
    • JPQL 쿼리 실행 - 플러시 자동 호출
      • JPQL이나 Criteria 같은 객체지향 쿼리를 호출할 때도 플러시가 실행된다.

  • URL vs URI
    • URI
      • 통합 자원 식별자(Uniform Resource Identifier, URI)는 인터넷에 있는 자원을 나타내는 유일한 주소
      • URL과 URN은 URI의 종류이다.
    • URL
      • 네트워크 상에서 자원이 어디 있는지를 알려주기 위한 규약

  • lombok 관련
    • @EqualsAndHasCode에서 of를 사용하는가
      • entity 간의 연관관계가 있을 때 stackOverflow가 발생할 수 있으므로 특정 필드를 설정한다.
      • 다른 Entity를 묶는 것은 좋지 않다.
    • @Builder를 사용할 때 @AllArgsConstructor가 필요한가
      • @Builder를 사용하면 기본 생성자가 추가 되지 않는다.
        • Default 생성자로 추가된다.
      • 다른 패키지에서 객체를 만들기 어렵기 때문
    • @Data를 쓰지 않는 이유
      • 상호 참조 때문에 stackOverflow 발생 할 수 있음
    • 애노테이션 줄일 수 없나
      • lombok은 메타 에노테이션을 지원하지 않는다.
      • 아직은 줄일 수 없다.

  • MockMvc
    • 웹 서버를 띄우지 않아서 테스트의 속도가 빠르다.
    • DispatcherServlet를 만들어야 하기 때문에 단위 테스트 보다는 느리다.

  • 스프링 시큐리티
    • 웹 시큐리티

      • 웹 요청에 보안 인증
      • Filter 기반 시큐리티
      • Sevlet Filter와 연관
    • 메소드 시큐리티

      • 메소드가 호출되었을 때 보안 인증
      • AOP
    • 동작 방식

      1. 웹 요청이 들어온다.
      2. 요청을 Servlet Filter가 가로채서 Bean으로 등록되어 있는 SecurityInterceptor 쪽으로 요청을 보낸다.
      3. SecurityInterceptor 에 요청이 들어오면 인증 정보를 확인한다.
        • 인증 정보를 SecurityContext Holder 라는 Thread Local 구현체에서 가져온다.
        • Thread Local : 한 쓰레드 내에서 공유하는 자원
        • 종류
          • Filter SecurityInterceptor
          • Method SecurityInterceptor
      4. 3번 결과 로그인이 안되어 있다면(인증 정보가 없다면) AuthenticationManager를 사용하여 로그인한다.
        • 로그인 시 UserDetailsService, PasswordEncoder 의 2개의 인터페이스 사용
        • Basic Authentication : 인증 요청 해더에 Authentication, basic, (userName + userPassword를 인코딩 한 문자열) 담아서 보낸다**.**
          • UserDetailsService에서 password를 가져오고 PasswordEncoder를 통해 입력한 password가 맞는지 확인한다.
          • 맞으면 Authentication 객체를 만들어서 SecurityContext Holder 에 저장한다.
      5. 요청한 리소스에 대한 권한이 있는지 확인한다. (AccessDecisionManager 사용)
        • User의 Role을 이용한다.
        • 인증된 Account가 해당 리소스를 사용할 권한이 있는지 확인한다.
    • User 객체 생성

      1. 사용자 정보를 웹에서 받는다. (Account 객체)
      2. passwordEncoder를 통해 password를 인코딩 하고 Account객체를 저장
      public Account saveAccount(Account account) {
          account.setPassword(this.passwordEncoder.encode(account.getPassword()));
          return this.accountRepository.save(account);
      }
      
    • User 객체 인증

      1. loadUserByUsername 메소드로 username 을 기준으로 사용자 정보를 찾는다.

        	public class AccountService implements UserDetailsService {
        		...
        		@Override
        		public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        		    Account account = this.accountRepository.findByEmail(username)
        	          .orElseThrow(() -> new UsernameNotFoundException(username));
        		    return new AccountAdaptor(account);
        		}
        		...
        	}
        
      2. 찾은 Account 객체를 AccountAdaptor 객체로 변환한다.

        public class AccountAdaptor extends User {
        	...
        	public AccountAdaptor(Account account) {
        	    super(account.getEmail(), account.getPassword(), authorities(account.getRoles()));
        	    this.account = account;
        	}
        	...
        }
        
        1. User 객체를 만들기 위해 객체 생성시 (사용자 id, 사용자 pw, 인, authorities 정보)
          • Role을 authorities로 변환 해야한다.
        public class AccountAdaptor extends User {
        	...
        	public AccountAdaptor(Account account) {
        	    super(account.getEmail(), account.getPassword(), authorities(account.getRoles()));
        	    this.account = account;
        	}
        	...
        	private static Collection<? extends GrantedAuthority> authorities(Set<AccountRole> roles) {
        	    return roles.stream()
        	        .map(r -> new SimpleGrantedAuthority("ROLE_" + r.name()))
        	        .collect(Collectors.toSet());
        	}
        	...
        }
        

  • 스프링 시큐리티 OAuth 2.0

    • AuthorizationServer : OAuth2 토큰 발행(/oauth/token) 및 토큰 인증(/oauth/authorize)
    • ResourceServer : 리소스 요청 인증 처리 (OAuth 2 토큰 검사)
  • UserDetailsServiceAutoConfiguration

    • 초기 스프링 시큐리티 설정 시 기본 user id와 password를 제공

    • SecurityProperties 에서 기본 User 정보와 Password를 가져온다.

    • application.properties 에서 설정가능 (좋지 않다.)

      spring.security.user.name=admin
      spring.security.user.password=admin
      spring.security.user.roles=ADMIN
      
  • UserDetailsService

    • Authentication을 관리할 때 DAO interface를 통해 유저 정보를 사용
      • 저장소에 들어있는 유저정보를 사용하여 인증
    • 역할 : user 정보를 받아와서 저장소로부터 user와 일치하는 정보를 찾고 UserDetails 으로 반환
      • 일치하는 정보가 없으면 UsernameNotFoudException() 발생
      • User.builder()를 사용하여 UserDetails 타입의 객체 생성
  • PasswordEncdoer

    • 사용을 위해 Bean으로 등록해야 한다.
    • password 를 특정한 encoder로 encode
    • 다양한 password encoder 지원
    @Bean
    public PasswordEncoder passwordEncoder() {
    	return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
    
  • 스프링 시큐리티 설정

    1. 시큐리티 관련 설정을 하기위해 SecurityConfig 객체 생성 후 WebSecurityConfigurerAdapter를 상속 받는다.

      • Spring boot의 자동 설정이 해제
    2. AppConfig 객체를 생성하여 기본 사용자 추가 및 passwordEncoder 빈으로 추가

    3. UserDetailServicepasswordEncoder를 주입받는다.

      • AccountService == UserDetailService
    4. TokenStore 을 빈으로 등록

      • OAuth Token을 저장하는 곳
      • 저장소 선택을 하고 return 한다.
    5. AuthenticationManager 를 Bean으로 노출시킨다.

      @Bean
      @Override
      public AuthenticationManager authenticationManagerBean() throws Exception {
          return super.authenticationManagerBean();
      }
      
    6. AuthenticationManager 를 재정의 하기 위해 builder를 만든다.

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(accountService)
                .passwordEncoder(passwordEncoder);
    }
    
    1. Filter 설정 (Web에서 걸러 낼지)
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().mvcMatchers("/docs/index.html");
        web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }
    
    • 정적 리소스와 restDocs 들은 Filter 설정에서 무시

      1. 스프링 시큐리티는 적용하되 http로 걸러낼수 있다.
    • 일단 스프링 시큐리티 안으로 들어온 상태

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .anonymous() // 익명 사용자 허용
                    .and()
                .formLogin() // Form 인증을 사용한다.
                    .and()
                .authorizeRequests() // 허용할 메소드가 있는데
                    .mvcMatchers(HttpMethod.GET, "/api/**").authenticated() // /api/ 라고 시작하는 모든 GET 요청은 인증이 필요하지 않다.
                    .anyRequest().authenticated(); // 나머지 요청도 인증이 필요하다.
    }
    
    1. OAuth 인증 서버 설정
    • 서버가 설정되면 인증 토큰을 발급 받을 수 있어야 한다.

    • AuthServerConfig 객체 생성 후 AuthorizationServerConfigurerAdapter 를 상속 받는다.

      • @EnableAuthorizationServer 어노테이션 추가
      • 메소드에서 사용할 객체를 주입 받는다.
      ...
      private final PasswordEncoder passwordEncoder;
      private final AuthenticationManager authenticationManager;
      private final AccountService accountService;
      private final TokenStore tokenStore;
      private final AppProperties appProperties;
      
      public AuthServerConfig(PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager, AccountService accountService, TokenStore tokenStore, AppProperties appProperties) {
          this.passwordEncoder = passwordEncoder;
          this.authenticationManager = authenticationManager;
          this.accountService = accountService;
          this.tokenStore = tokenStore;
          this.appProperties = appProperties;
      }
      ...
      
      • 3 개의 configure 메소드를 @Override 하여 정의한다.

        • security configure에는 passwordEncoder 설정
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.passwordEncoder(passwordEncoder);
        }
        
        • client configure에는
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory() // in-memory로 token을 관리
                    .withClient(appProperties.getClientId()) // client 설정
                    .authorizedGrantTypes("password", "refresh_token") // grant type 설정
                    .scopes("read", "write") // scope 설정
                    .secret(this.passwordEncoder.encode(appProperties.getClientSecret())) // 앱의 secret 설정
                    .accessTokenValiditySeconds(10 * 60) // access token 의 유효기간 설정
                    .refreshTokenValiditySeconds(6 * 10 * 60); // token 을 refresh 하는 시간
        }
        
        • endpoints configure 에는
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)
                    .userDetailsService(accountService)
                    .tokenStore(tokenStore);
        }
        
    • 인증 토큰을 받기 위해서는 Grant Type을 설정해야 한다.

      • password 방식 : 홉이 1번이다.

        • 필요 파라미터
          • grant_type : request parameter 형태
          • username : request parameter 형태
          • password : request parameter 형태
          • client_id : 헤더로 넣을 수 있다. bearer authentication 형태
          • client_secret : 헤더로 넣을 수 있다. bearer authentication 형태
        this.mockMvc.perform(post("/oauth/token")
                .with(httpBasic(appProperties.getClientId(), appProperties.getClientSecret()))
                .param("username", appProperties.getUserUsername())
                .param("password", appProperties.getUserPassword())
                .param("grant_type", "password"))
        
        • password를 직접 입력하기 때문에 해당 서비스를 지원하고 있는 앱에서만 사용 가능 (서드파티 이용x)
    1. ResourceServer 설정
    • OAuthServer랑 연동하여 사용
    • 외부 요청이 resource에 접근 할때 OAuthServer에 토큰을 보내 토큰을 확인 후 접근 허용 또는 접근 제한
    • ResourceServerConfig 객체 생성 후 ResourceServerConfigurerAdapter 를 상속
    • configure 메소드 오버라이딩하여 재정의
    • resources configure 설정
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    		// resource id 설정
        resources.resourceId("event");
    }
    
    • resources http 설정
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .anonymous() // 익명 사용자 접근을 허용
                .and()
            .authorizeRequests() // request 설정
              .mvcMatchers(HttpMethod.GET, "/api/**") // /api/로 시작하는 GET 요청은
                .permitAll() // 모두 허용한다.
    	        .anyRequest() // 다른 요청들은
    		        .authenticated() // 인증이 필요
    	        .and()
            .exceptionHandling() // 인증 잘못되거나 권한이 없는경우
    		      .accessDeniedHandler(new OAuth2AccessDeniedHandler()); // 권한이 없는경우 에러 처리 위임
    }
    
    1. 현재 사용자 조회
    • @AuthenticationPrincipal 를 사용하여 User 객체를 받을 수 있다.

      • Account 객체로 받기위해 expression을 사용하여 null case 처리
      @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
      

  • spring security

    • principal : 인증된 사용자가 있다면 principal interface 타입으로 저장하여 사용

    • security를 의존성 추가하면 기본으로 id는 user pw는 콘솔에 출력된다.

    • WebSecurityConfigurerAdapter를 상속받아 security 설정

      Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.authorizeRequests()
                      .mvcMatchers("/","/info").permitAll()
                      .mvcMatchers("/admin").hasRole("ADMIN")
                      .anyRequest().authenticated();
          }
      }
      
      • 사용자 계정 추가

        • {noop}은 spring security의 기본 password encoder
          • 암호화를 하지 않았다.
        • db에 들어가는 값이다.
        @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.inMemoryAuthentication()
                        .withUser("keesum").password("{noop}123").roles("USER").and()
            }
        
      • spring security는 password 패턴을 요구

        • {PASSWORD_ENCODER}비밀번호
      • PasswordEncoder 사용

        • 여러가지 인코더 사용가능
        @Bean
        public PasswordEncoder passwordEncoder() {
            return PasswordEncoderFactories.createDelegatingPasswordEncoder();
        }
        
  • Authentication

    • Principal과 GrantAuthority 제공
  • 스프링 시큐리티 과정

    1. 서블릿 필터에 요청이 들어온다.
      • 서블릿 필터 중 DeligatingFilterProxy 필터는 스프링 부트가 자동으로 등록해준다.
        • 부트를 사용하지 않으면 AbstractSecurityWebApplicationInitializer를 상속하여 만든다.
    2. DeligatingFilterProxy 특정한 Bean 이름으로 해당 요청의 필터 처리를 위임한다.
      • 해당 Bean은 FilterChainProxy 로 만든다.
        • Bean의 이름은 springSecurityFilterChain
        • 여러 필터들을 체인 형태로 가지고 있다.
          • 체인들은 Websecurity 를 만든다. (HttpSecurity도 같이 사용해서 만든다.)
          • 때문에 만든 Security 설정 파일이 WebSecurityConfigurerAdapter를 상속한다.
    • 인증
      • AuthenticationManager 가 중요 (ProviderManager가 구현체)
      • ProviderManager 는 다른 AuthenticationProvider를 사용하여 인증을 처리
        • AuthenticationProviderDaoAuthenticationProviderUserDetailsService라는 인터페이스를 사용하여 데이터에서 읽어온 유저정보를 사용하여 인증을 한다.
      • 인증이 성공하면 인증 정보를 SecurityContextHolder에 넣어놓는다.
        • SecurityContextHolderSecurityContextAuthenticationPrincipal , GrantedAuthorities
        • 위 정보들은 Session에 저장된다.
          • 저장된 Session은 SecurityContextPersistenceFilter에 의해 읽혀진다.
    • 인가
      • FilterSecurityInterceptor 인가를 처리하는 인터셉터
      • FilterSecurityInterceptorAccesDecisionManager를 사용하여 인가를 처리한다.
        • 현재 SecurityContextHolderAuthentication이 특정 리소스에 접근 가능한지 확인
          • 확인방법 : AffirmativeBased (기본 전략)
          • AffirmativeBased 가 사용하는 VoterWebExpressionVoter를 사용
          • WebExpressionVoterSecurityExpressionHandler를 사용하여 Exception 처리
            • RoleHierarchyImpl 를 사용하여 커스터마이징 가능
  • WebAsyncManagerIntegrationFilter

    • 시큐리티 필터 중 가장 위에 있는 필터

    • 스프링 MVC async 핸들러를 지원

    • 쓰레드간 SecurityContext를 공유

    • @Async : 특정 Bean안의 메소드를 호출할 때 새로 Thread를 만들어 처리

      • Application Class에 @EnableAsync 추가
      • 기본만 설정하면 SecurityContext를 공유하지 않는다.
        • SecurityConfig Class에 아래 코드를 추가하여 SecurityContext를 공유할 수 있다.
      SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
      
  • SecurityContextPersistenceFilter

    • 여러 요청 간의 SecurityContext 를 공유
    • HttpSession에서 기존에 만들어져 있는SecurityContext 를 읽어온다.
      • 없다면 비어있는 SecurityContext 를 만든다.
  • HeaderWriterFilter

    • 응답 헤더에 시큐리티 관련 헤더를 추가해주는 필터
    • 설정할 일이 거의 없다.
  • CsrfFilter

    • CSRF 어택 : 인증된 유저의 계정을 사용해 악의적인 변경 요청을 만들어 보내는 기법
      • CSRF 토큰을 사용하여 방어
  • LogoutFilter

    • 로그아웃을 처리
    • LogoutHandler 설정
  • UsernamePasswordAuthenticationFilter

    • 로그인 요청이 서버로 오면 UsernamePasswordAuthenticationFilterAuthenticationManager 를 사용하여 인증을 진행
      • usernamepassword를 이용하여 AuthenticationToken을 만든다.
      • AuthenticationToken 를 확인한다.

OAuth Roles

  • resource owner
    • protected resource에 대한 액세스 권한을 부여 할 수있는 엔티티.
    • resource owner가 개인인 경우 이를 end-user라고합니다.
    • 리소스의 소유주 (일반 사용자)
  • resource server
    • access token을 사용하여 protected resource 요청을 수락하고 응답 할 수있는 protected resource를 호스팅하는 서버.
    • google, facebook, ... (데이터를 가지고 있는 서버)
  • client
    • resource owner를 대신하여 권한을 부여하여 보호 된 자원 요청을 하는 응용 프로그램.
    • "클라이언트"라는 용어는 특정 구현 특성 (예 : 응용 프로그램이 서버, 데스크톱 또는 기타 장치에서 실행되는지 여부)을 의미하지 않습니다.
    • 우리가 만든 애플리케이션 서버
  • authorization server
    • resource owner를 성공적으로 인증하고 권한을 얻은 후 서버가 클라이언트에 액세스 토큰을 발행합니다.
    • 인증과 관련된 처리를 전담하는 서버

OAuth

  • 스프링 2.x 이면 설정을 자동으로 해준다.

  • OAuth를 사용하기 전에 OAuth 2.0 credential를 획득해야 한다.

  • redirect URI : 최종 사용자의 사용자 에이전트가 Google에서 인증 한 후 동의 페이지의 OAuth 클라이언트에 대한 액세스 권한을 부여한 후 다시 리디렉션되는 애플리케이션의 경로입니다.

    • default : {baseUrl}/login/oauth2/code/{registrationId}
  • Google에서 받은 OAuth Client 정보를 application.yml에 저장

    spring:
      security:
        oauth2:
          client:
            registration:   
              google:   
                client-id: google-client-id
                client-secret: google-client-secret
    
  • OAuth 인증 과정

    1. 용어 설명
    • Client : 애플리케이션 서버 (Spring boot application)
    • Resource Server : 데이터를 가지고 있는 서버 (Google, ...)
    • Resource Owner : Resource의 주인 (사용자)
    1. ClientResource Server를 사용하기 위해 Resource Server로 부터 승인을 받는다. (register)
      • ClientClient Id, Client Secret, Authrized redirect URI를 보유하고 있어야함
      • Scope를 설정하여 Resource Server의 기능을 선택적으로 사용
      • 결과 : ClientResource ServerClient Id, Client Secret, Authrized redirect URI를 모두 가지고 있다.
    2. Resource OwnerClient에게 Resource Server의 자원을 사용한다는 요청을 보냄
      • Client는 인증을 위해 Resource Owner에게 Resource Server의 링크를 보낸다.
      • Resource Server가 현재 Resource Owner의 로그인 여부를 판단 후 로그인 처리
        • 로그인이 되어 있지않으면 로그인 창을 응답
      • 결과 : Resource OwnerResource Server로 로그인 완료
    3. Client가 보낸 링크의 redirect_uri값과 Resource Server에서 가지고 있는 redirect_uri 값을 비교
      • 서로 값이 다르면 더이상 진행하지 않고 멈춘다.
      • 값이 같으면 2번에서 ClientResource Owner에게 보낸 링크 중 scope를 확인
        • Resource ServerResource Owner에게 해당 scope의 정보를 제공하기 위해 동의를 얻는다.
      • 결과 : Resource Server에 user_id와 scope 값을 저장
    4. Resource ServerResource Owner에게 redirect URL값과 authorization code를 전송
      • 결과 : Resource Owner는 authorization code를 담아서 Client에게 redirect URL 주소로 접근
    5. ClientResource Server로 접근
      • 요청 URL과 grant_type(authorization_code), code, redirect_uri, client_id, client_secret 값이 전송
    6. Resource Server는 5번 요청에 담긴 값을 검사
      • 5번 요청의 authorization_code값과 Resource Server에 저장되어있는 authorization_code 값을 비교하여 Client Id를 획득
      • 5번 요청의 client_Id, client_secret 값을 비교
      • 전부 일치하면 Resource Server는 authorization_code 값을 지운다. (다시 인증을 안하려고)
    7. Resource Server는 Access Token 발급하고 Client에게 Access Token 전송
      • Access Token 값으로 해당 user_id는 어떤 scope 값이 있는지 확인 가능
  • OIDC

    • OpenID Connect
    • OpenID Connect가 OAuth 2.0을 기반으로 상위계층에서 좀 더 간편하게 인증을 처리할 수 있도록 고안
    • OpenID Connect는 Access 토큰과 함께 ID 토큰을 전달합니다. 이 JWT(JSON Web Tokens)를 통해 암호화 된 토큰 안에 사용자 정보를 비롯한 다양한 정보를 HTTP 헤더의 최대 사이즈인 4KB 이내로 저장
    • Access 토큰을 사용하여 한 번 더 OAuth 2.0 API를 호출할 필요 없이 사용자 정보가 담긴 ID 토큰을 복호화 하여 바로 사용할 수 있게 됩니다.
  • OAuth 인증 과정 in Spring boot

    1. OAuth2AuthorizationRequestRedirectFilter 접근

      • OAuth 2.0 Authorization Request 를 생성
      • OAuth를 사용한 authorization request에 반응한다.
        • /oauth2/authorization/{registrationId}
    2. OAuth2LoginAuthenticationFilter 클래스에서 attemptAuthentication() 호출하여 인증 시도

      • authenticationRequest : 인증을 위한 정보

        https://s3-us-west-2.amazonaws.com/secure.notion-static.com/12c3ec00-69a4-4f4b-b96c-9f66faf8eb5e/Untitled.png

    3. 인증을 위한 ProvideManagerOidcAuthorizationCodeAuthenticationProviderauthenticate() 실행

      • OidcUserServiceloadUser() 실행

        https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4e63157a-15c8-4d5b-8484-4aa2b9fbcf7e/Untitled.png

        • loadUser() 실행 중 OidcUserAuthority 로 ROLE 설정

          • 기본값은 ROLE_USER

          https://s3-us-west-2.amazonaws.com/secure.notion-static.com/02fb3482-f0af-4c56-9d2a-f54a8019d82c/Untitled.png

        • SimpleGrantedAuthority 타입으로 SCOPE 설정

          https://s3-us-west-2.amazonaws.com/secure.notion-static.com/210d8242-f5fa-4642-a22a-19d84350fa8c/Untitled.png

        • OidcUser 생성 (DefaultOidcUser 타입)

          • 생성시 authorities, Id Token, userInfo를 파라미터로
          • name은 idToken의 sub 값으로 설정
            • sub 값은 JWT의 principal을 식별
        • 결과로 oidcUser 를 return

      • OidcUser의 authority를 GrantedAuthoritiesMapper 로 매핑

      • OAuth2LoginAuthenticationToken 객체 생성 후 return

        https://s3-us-west-2.amazonaws.com/secure.notion-static.com/949606e2-a0a1-4523-999b-6a03d80661ef/Untitled.png

    4. 3번의 결과를 이용하여 로 OAuth2AuthenticationToken 를 생성

      • 생성자 파라미터로

        https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dbd5d22b-06b3-49c5-8516-b2286176c89f/Untitled.png

    5. 3번, 4번 리턴 값을 사용하여 OAuth2AuthorizedClient 를 생성

      https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c7281ca4-1b0e-41ed-a22c-2fd24f2aebfd/Untitled.png

    6. 5번에서 생성한OAuth2AuthorizedClientOAuth2AuthorizedClientRepository 에 저장

      • OAuth2AuthorizedClientService 를 implement를 하여 customize 했다면 @OverridesaveAuthorizedClient() 를 실행한다.
    7. OAuth2LoginAuthenticationFilter 의 결과로 4번에서 만든 OAuth2AuthenticationToken 을 리턴