목차

    인덱스란?

    insert, update, delete (Command)의 성능을 희생하고 대신 select (Query)의 성능을 향상시킵니다. 여기서 주의하실 것은 update, delete 행위가 느린것이지, update, delete를 하기 위해 해당 데이터를 조회하는것은 인덱스가 있으면 빠르게 조회가 됩니다.
    인덱스가 없는 컬럼을 조건으로 update, delete를 하게 되면 굉장히 느려 많은 양의 데이터를 삭제 해야하는 상황에선 인덱스로 지정된 컬럼을 기준으로 진행하는것을 추천드립니다.

    B-tree 인덱스

    • 인덱스 탐색은 Root -> Branch -> Leaf -> 디스크 저장소 순으로 진행됩니다.
      • 예를 들어 Branch (페이지번호 2) 는 dept_no가 d001이면서 emp_no가 10017 ~ 10024까지인 Leaf의 부모로 있습니다.
      • 즉, dept_no=d001 and emp_no=10018로 조회하면 페이지 번호 4인 Leaf를 찾아 데이터파일의 주소를 불러와 반환하는 과정을 하게 됩니다.
    • 인덱스의 두번째 컬럼은 첫 번째 컬럼에 의존해서 정렬되어 있습니다.
      • 즉, 두번째 컬럼의 정렬은 첫번째 컬럼이 똑같은 열에서만 의미가 있습니다.
      • 만약 3번째, 4번째 인덱스 컬럼도 있다면 두번째 컬럼과 마찬가지로 3번째 컬럼은 2번째 컬럼에 의존하고, 4번째 컬럼은 3번째 컬럼에 의존하는 관계가 됩니다.
    • 디스크에서 읽는 것은 메모리에서 읽는것보다 성능이 훨씬 떨어집니다.
      • 결국 인덱스 성능을 향상시킨다는 것은 디스크 저장소에 얼마나 덜 접근하게 만드느냐, 인덱스 Root에서 Leaf까지 오고가는 횟수를 얼마나 줄이느냐에 달려있습니다.
    • 인덱스의 갯수는 3~4개 정도가 적당합니다.
      • 너무 많은 인덱스는 새로운 Row를 등록할때마다 인덱스를 추가해야하고, 수정/삭제시마다 인덱스 수정이 필요하여 성능상 이슈가 있습니다.
      • 인덱스 역시 공간을 차지합니다. 많은 인덱스들은 그만큼 많은 공간을 차지합니다.
      • 특히 많은 인덱스들로 인해 옵티마이저가 잘못된 인덱스를 선택할 확률이 높습니다.

    인덱스 키 값의 크기

    InnoDB (MySQL)은 디스크에 데이터를 저장하는 가장 기본 단위를 페이지라고 하며, 인덱스 역시 페이지 단위로 관리 됩니다.
    (B-Tree 인덱스 구조에서 Root, Branch, Leaf 참고)

    페이지는 16KB 로 크기가 고정되어 있습니다.

    만약 본인이 설정한 인덱스 키의 크기가 16 Byte 라고 하고, 자식노드(Branch, Leaf)의 주소(위 인덱스 구조 그림 참고)가 담긴 크기가 12 Byte 정도로 잡으면, 16*1024 / (16+12) = 585로 인해 하나의 페이지에는 585개가 저장될 수 있습니다.
    여기서 인덱스 키가 32 Byte로 커지면 어떻게 될까요?
    16*1024 / (32+12) = 372로 되어 372개만 한 페이지에 저장할 수 있게 됩니다.

    조회 결과로 500개의 row를 읽을때 16byte일때는 1개의 페이지에서 다 조회가 되지만, 32byte일때는 2개의 페이지를 읽어야 하므로 이는 성능 저하가 발행하게 됩니다.

    인덱스의 키는 길면 길수록 성능상 이슈가 있습니다.

     

    인덱스 컬럼 기준

    먼저 말씀드릴 것은 1개의 컬럼만 인덱스를 걸어야 한다면, 해당 컬럼은 카디널리티(Cardinality)가 가장 높은 것을 잡아야 한다는 점입니다.

    카디널리티(Cardinality)란 해당 컬럼의 중복된 수치를 나타냅니다.
    예를 들어 성별, 학년 등은 카디널리티가 낮다고 얘기합니다.
    반대로 주민등록번호, 계좌번호 등은 카디널리티가 높다고 얘기합니다.

    인덱스로 최대한 효율을 뽑아내려면, 해당 인덱스로 많은 부분을 걸러내야 하기 때문입니다.
    만약 성별을 인덱스로 잡는다면, 남/녀 중 하나를 선택하기 때문에 인덱스를 통해 50%밖에 걸러내지 못합니다.
    하지만 주민등록번호나 계좌번호 같은 경우엔 인덱스를 통해 데이터의 대부분을 걸러내기 때문에 빠르게 검색이 가능합니다.

     

    여러 컬럼으로 인덱스 구성시 기준

    자 그럼 여기서 궁금한 것이 있습니다.
    여러 컬럼으로 인덱스를 잡는다면 어떤 순서로 인덱스를 구성해야 할까요?
    카디널리티가 낮은->높은순으로 구성하는게 좋을까요?
    카디널리티가 높은->낮은순으로 구성하는게 좋을까요?
    실제 실험을 통해 확인해보겠습니다.

    테스트 환경은 AWS EC2 Ubuntu 16.04를 사용했습니다.
    최대한 극적인 비교를 위해 메모리는 1G, 디스크는 마그네틱(SSD X)을 사용했습니다.

    테이블 형태는 아래와 같습니다.

    CREATE TABLE `salaries` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `emp_no` int(11) NOT NULL,
      `salary` int(11) NOT NULL,
      `from_date` date NOT NULL,
      `to_date` date NOT NULL,
      `is_bonus` tinyint(1) unsigned zerofill DEFAULT NULL,
      `group_no` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    첫번째 인덱스는 is_bonus, from_date, group_no순으로 카디널리티가 낮은순에서 높은순 (중복도가 높은 순에서 낮은순으로) 으로,
    두번째 인덱스는 group_no, from_date, is_bonus순으로 카디널리티가 높은순에서 낮은순 (중복도가 낮은 순에서 높은순으로) 으로 생성했습니다.

    CREATE INDEX IDX_SALARIES_INCREASE ON salaries 
    (is_bonus, from_date, group_no);
    
    CREATE INDEX IDX_SALARIES_DECREASE ON salaries 
    (group_no, from_date, is_bonus);

    사용한 쿼리

    select SQL_NO_CACHE * 
    from salaries 
    use index (IDX_SALARIES_INCREASE)
    where from_date = '1998-03-30' 
    and group_no in ('abcdefghijklmn10494','abcdefghijklmn3968', 'abcdefghijklmn11322', 'abcdefghijklmn13902', 'abcdefghijklmn100', 'abcdefghijklmn10406') 
    and is_bonus = true;
    
    select SQL_NO_CACHE * 
    from salaries 
    use index (IDX_SALARIES_DECREASE)
    where from_date = '1998-03-30' 
    and group_no in ('abcdefghijklmn10494','abcdefghijklmn3968', 'abcdefghijklmn11322', 'abcdefghijklmn13902', 'abcdefghijklmn100', 'abcdefghijklmn10406') 
    and is_bonus = true;

     

    즉, 여러 컬럼으로 인덱스를 잡는다면 카디널리티가 높은순에서 낮은순으로 (group_no, from_date, is_bonus) 구성하는게 더 성능이 뛰어납니다

     

    •  인덱스
      • 책의 제일 끝에 있는 찾아보기 (목차랑은 다른개념이다.)
      • 데이터 : 책의 내용 / 인덱스 : 찾아보기
      • 책의 찾아보기와 인덱스의 공통점 : 정렬
    • 프로그래밍 언어의 자료구조로 인덱스와 데이터 파일을 비교
      • SortedList : 인덱스와 같은 자료구조 
        • 저장되는 값을 항상 정렬된 상태로 유지하는 자료구조
        • 장점 : 이미 정렬되어 있기 때문에 아주 빠르게 원하는 값을 찾을 수 있음(Select)
      • ArrayList : 데이터와 파일과 같은 자료구조
        • 값을 저장되는 순서대로 그대로 유지하는 자료구조
    • DBMS의 인덱스는 데이터의 저장(Insert, update, delete) 성능을 희생하고 대신 데이터의 읽기 속도를 높이는 기능
      • Select 문장의 Where 조건절에 사용되는 모든 컬럼을 인덱스로 생성하면 저장성능이 떨어지고, 인덱스 크기가 커져 오히려 역효과
    • 인덱스 데이터 저장 방식(알고리즘) 별로 구분
      • B-tree 인덱스
        • 컬럼의 값을 번형하지 않고, 원래의 값을 이용하여 인덱싱
      • Hash 인덱스
        • 컬럼의 값으로 해시 값을 계산해서 인덱싱
        • 매우 빠른 검색을 지원

    실습으로 DB 인덱싱 알아보기

    실습1 : 모든 동물 테이블을 검색하고, nocope를 검색한다. 그리고 인덱싱 처리해서 실행시간을 비교한다.

    위와 같은 Animal 테이블이 있다고 가정하자.

    그리고 천만 건의 데이터 중를 모두 select count(*) from Animals; 해주고 또 nocope를 검색하는 쿼리를 날려준다.

    각각 3.6초, 4.5초가 걸렸다. 엄청 오래 걸린다...즉, 서비스에서는 못쓴다는 것이다. 

    인덱스 추가하기

    CREATE INDEX ANIMAL_NAME ON ANIMALS(NAME);

    동물 이름에 index를 추가해보자

    그러고 성능비교를 해보자 그랬더니 0.003초로 엄청나게 빨라졌다.

    어떻게 동작하는 것인가?

    B-Tree : Balanced Tree

    위의 그림에서 5를 찾고 싶다라고 가정하면 7과 비교해서 작은 왼쪽트리로 가고 만약 9를 찾고 싶다하고 하면 가운데 트리로 이동하는 개념이다. 

    위의 상황을 살펴보자. 동물이름은 비트리안에서 오름차순으로 정렬이 되어있다. 만약 nabi를 찾고 싶다면 meow와 비교해서 뒷편에 있을 테니 2번째 브랜치로 이동하여 탐색하는 개념이다.

     

    실습2 : 동물 테이블에서 키가 30cm~50cm 사이인 동물의 평균 키는?

    해당 쿼리를 실행 시켜보았다. 4.3초... 이것도 서비스에서는 못쓴다.

    인덱스 적용하기

    CREATE INDEX ANIMAL_WEIGHT ON ANIMALS(weight);

    몸무게에 where절이 걸렸으므로 weght에 인덱스를 걸어준다.

    하지만...

    같은 쿼리를 실행하면 4.2초로 성능 차이가 없다. weight가 전혀 인덱스를 안탄것인데 왜 그런지 알아보자.

    between 이므로 시작점 30과 끝점 50을 찾는다. 

    만약 위처럼 평균 키(height)가 아닌 평균 몸무게(weight)로 검색했다면 인덱스를 잘 타고 0.6초 만에 검색이 되었을 것이다. 왜냐면 해당 쿼리문을 실행 시키기 위한 모든 정보가 인덱스 안에 있기 때문이다.

    즉, 위의 쿼리는 키에 따른 몸무게 정보를 알기 위해서 full scan 과정을 또 겪었을 것이며 인덱스를 타지 않았다.

    해결 방법

    각 몸무게에 대한 키정보를 인덱싱한다. 

    다음과 같이 키, 몸무게 정보를 담은 인덱싱을 한다.

    CREATE INDEX animal_wh on Animal(weight,height)

    결과

    0.7초로 단축하며 인덱스를 타는 것을 알 수 있다.

    마지막으로 인덱스를 잘 타는지 확인하고 싶다면 analyze 키워드를 맨 앞에 쓰면 된다.

    key라는 컬럼에 animal_wh라는 인덱스를 이용했다고 명시한다.

    인덱스 타는지 알아보는 방법

    https://jsj0903.tistory.com/5

     

    [CLOUD] Iass vs Pass vs Sass

    💡클라우드 컴퓨팅이란? 클라우드 컴퓨팅은 스토리지, 플랫폼, 애플리케이션,네트워크 등의 IT 자원들을 사용자가 소유하지 않고 통신망/인터넷을 통해 제공하여 소비자가 필요한 만큼 빌려

    jsj0903.tistory.com

     

     참고 사항

     

    [DB] 인덱스란? - (1) 개념, 장단점, 쓰는 이유

    Index(이하 인덱스)는 DB를 다루다 보면 필연적으로 듣는 단어이다. 본격적으로 글에 들어가기 전에 인덱스에 대해 간단하게 설명하자면, DB 데이터 조회 성능 향상을 위해 사용한다. 대용량 데이

    siahn95.tistory.com

     

    https://jojoldu.tistory.com/243

     

    [mysql] 인덱스 정리 및 팁

    MySQL 인덱스에 관해 정리를 하였습니다. MySQL을 잘 알아서 정리를 한것이 아니라, 잘 알고 싶어서 정리한 것이라 오류가 있을수도 있습니다. 1. 인덱스란? 인덱스 == 정렬 인덱스는 결국 지정한 컬

    jojoldu.tistory.com

    https://zorba91.tistory.com/292

     

    [MySQL] 인덱스(Index) 정리

    인덱스(Index) 정리 인덱스를 알아보기 전에 풀 스캔(Full Scan)과 레인지 스캔(Range Scan)을 이해해야 한다. 풀 스캔(Full Scan) & 레인지 스캔(Range Scan) 풀 스캔 : 테이블에 포함된 레코드를 처음부터 끝

    zorba91.tistory.com

     

     

    'Back-end > DB' 카테고리의 다른 글

    [DB] 결합 인덱싱  (0) 2022.05.14
    [DB] Dababase Sharding 이란?  (0) 2022.05.05
    인덱스(Index) 란?  (0) 2022.05.05

    목차

      REST의 정의

      REST API란 REST를 기반으로 만들어진 API를 의미합니다. REST란 무엇일까?
      • REST(Representational State Transfer)의 약자로 자원을 이름으로 구분하여 해당 자원의 상태를 주고받는 모든 것이라는 뜻이다. 
      • 다시 말하면 웹에 존재하는 모든 자원(이미지, 동영상, DB자원)에 고유한 URI를 부여해 활용 하는 것 즉 자원에 대한 주소를 지정하는 방법론을 REST라고 한다.
      REST 란!
      - HTTP URI(Uniform Resource Identifier)을 통해 자원(Resource)를 명시한다.
      - HTTP Method(POST, GET, DELTE)를 통해 해당 자원(URI)에 대한  CRUD Operation을 적용 하는 것을 의미한다.

      우리는 왜 RESTful APIs를 만드는 것일까?

      • RESTful APIs 개발하는 가장 큰 이유는 Client Side를 정형화된 플랫폼이 아닌 모바일, PC, 어플리케이션 등 플랫폼에 제약을 두지 않는 것을 목표로 했기 때문 입니다.
      • 즉, 2010년 이전만 해도 Server Side에서 데이터를 전달해주는 Client 프로그램의 대상은 PC 브라우저로 그 대상이 명확 했다. 그렇다 보니 그냥 JSP ASP PHP 등을 잉요한 웹페이지를 구성하고 작업을 진행하면 됐다.
      • 하지만 스마트 기기들이 등장하면서 TV, 스마트 폰, 테블릿 등 Client 프로그램이 다양화 되고 그에 맞춰 Server를 일일이 만다는 것이 꽤 비효율적인 일이 되어 버렸다.
      • 이런 과정에서 개발자들은 Client Side를 전혀 고려하지 않고 메시지 기반, XML, JSON과 같은 Client에서 바로 객체로 치환 가능한 형태의 데이터 통신을 지향하게 되면서 Server와 Client의 역할을 분리하게 되었다.

      이런 변화를 겪으면서 필요해진 것은 HTTP 표준 규약을 지키면서 API를 만드는 것이다.

      CRUD Operation 이란?

      CRUD 기본적인 데이터 처리 기능인 Create(생성), Read(읽기), Update(갱신), Delete(삭제)를 묶어서 일컫는 말

      • Create : 데이터 생성(POST)
      • Read : 데이터 조회(GET)
      • Update : 데이터 수정(PUT)
      • Delete : 데이터 삭제(DELETE)

      REST 구성 요소

      REST는 다음과 같은 3가지로 구성이 되어있다. 

      1. 자원(Resource) : HTTP URI
      2. 자원에 대한 행위(Verb) : HTTP Method
      3. 자원에 대한 행위의 내용 (Representations) : HTTP Message Pay Load

      1. 자원 (Resource) URL

      • 모든 자원에 고유한 ID가 존재하고, 이 자원은 Server에 존재한다.
      • 자원을 구별하는 ID는 /orders/order_id/1 와 같은 HTTP URI 이다.

      2. 행위 (Verb) - Http Method

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

      3. 표현 (Representaion of Resource)

      • Client가 자원의 상태 (정보)에 대한 조작을 요청하면 Server는 이에 적절한 응답 (Representation)을 보낸다
      • REST에서 하나의 자원은 JSON, XML, TEXT, RSS 등 여러 형태의 Representation으로 나타낼 수 있다.
      • 현재는 JSON으로 주고 받는 것이 대부분이다.

      REST의 특징

      1. Server-Client(서버-클라이언트 구조)
      2. Stateless(무상태)
      3. Cacheable(캐시 처리 가능)
      4. Layered System(계층화)
      5. Uniform Interface(인터페이스 일관성)

        1. Uniform Interface(일관된(=통합된) 인터페이스)

      Uniform Interface란, Resource(URI)에 대한 요청을 통일되고, 한정적으로 수행하는 아키텍처 스타일을 의미합니다. 이것은 요청을 하는 Client가 플랫폼(Android, Ios, Jsp 등) 에 무관하며, 특정 언어나 기술에 종속받지 않는 특징을 의미합니다. 이러한 특징 덕분에 Rest API는 HTTP를 사용하는 모든 플랫폼에서 요청가능하며, Loosely Coupling(느슨한 결함) 형태를 갖게 되었습니다.
      -> 어떤 장비에도 종속되지 않는다. 아이폰, 안드로이드, 웹..등
        2. Stateless(무상태성)
      서버는 각각의 요청을 별개의 것으로 인식하고 처리해야하며, 이전 요청이 다음 요청에 연관되어서는 안됩니다. 그래서 Rest API는 세션정보나 쿠키정보를 활용하여 작업을 위한 상태정보를 저장 및 관리하지 않습니다. 이러한 무상태성때문에 Rest API는 서비스의 자유도가 높으며, 서버에서 불필요한 정보를 관리하지 않으므로 구현이 단순합니다. 이러한 무상태성은 서버의 처리방식에 일관성을 부여하고, 서버의 부담을 줄이기 위함입니다.

      -> 각각의 요청이 별개여야 한다. 

        3. Cacheable(캐시 가능)
      Rest API는 결국 HTTP라는 기존의 웹표준을 그대로 사용하기 때문에, 웹의 기존 인프라를 그대로 활용할 수 있습니다. 그러므로 Rest API에서도 캐싱 기능을 적용할 수 있는데, HTTP 프로토콜 표준에서 사용하는 Last-Modified Tag 또는 E-Tag를 이용하여 캐싱을 구현할 수 있고, 이것은 대량의 요청을 효울척으로 처리할 수 있게 도와줍니다.
      -> HTTP라는 기존의 웹 표준을 사용하기 때문에 캐시도 사용가능하다.
        4. Client-Server Architecture (서버-클라이언트 구조)
      Rest API에서 자원을 가지고 있는 쪽이 서버, 자원을 요청하는 쪽이 클라이언트에 해당합니다. 서버는 API를 제공하며, 클라이언트는 사용자 인증, Context(세션, 로그인 정보) 등을 직접 관리하는 등 역할을 확실히 구분시킴으로써 서로 간의 의존성을 줄입니다.
      -> 서버-클라이언트의 역할이 확실히 구분된다. 요청, 응답 구조
        5. Self-Descriptiveness(자체 표현)
      Rest API는 요청 메세지만 보고도 이를 쉽게 이해할 수 있는 자체 표현 구조로 되어있습니다. 아래와 같은 JSON 형태의 Rest 메세지는 http://localhost:8080/board 로 게시글의 제목, 내용을 전달하고 있음을 손쉽게 이해할 수 있습니다. 또한 board라는 데이터를 추가(POST)하는 요청임을 파악할 수 있습니다.
      ->(POST) Rest API 요청 메시지 + body(JSON)을 보고 무슨 내용인지 파악이 가능하다.(=자체표현)
      HTTP POST , http://localhost:8080/board
      {
          "board":{
          "title":"제목",
          "content":"내용"
         }
        
      }

        6. Layered System(계층 구조)

      Rest API의 서버는 다중 계층으로 구성될 수 있으며 보안, 로드 밸런싱, 암호화 등을 위한 계층을 추가하여 구조를 변경할 수 있습니다. 또한 Proxy, Gateway와 같은 네트워크 기반의 중간매체를 사용할 수 있게 해줍니다. 하지만 클라이언트는 서버와 직접 통신하는지, 중간 서버와 통신하는지 알 수 없습니다.

      -> 결국 클라이언트<->서버간 통신인데 중간 과정을 클라이언트가 알 수 없다. 

      REST API 정의

      REST API란 REST의 원리를 따르는 API를 의미한다. 즉 리소스(HTTP URI로 정의됨)를 어떻게 하겠다.(HTTP Method + payload)를 구조적으로 표햔하는 방법이다.

      REST API 설계 예시
      더보기

      1. URI는 명사보다는 동사를, 대문자보다는 소문자를 사용하여야 한다.

      Bad Example http://www.naver.com/Running/
      Good Example  http://www.naver.com/run/  

       

      2. 마지막에 슬래시 (/)를 포함하지 않는다.

      Bad Example http://naver.com/test/  
      Good Example  http://naver.com/test

       

      3. 언더바 대신 하이폰을 사용한다.

      Bad Example http://naver.com/test_blog
      Good Example  http://naver.com/test-blog  

       

      4. 파일확장자는 URI에 포함하지 않는다.

      Bad Example http://naver.com/photo.jpg  
      Good Example  http://naver.com/photo  

       

      5. 행위를 포함하지 않는다.

      Bad Example http://naver.com/delete-post/1  
      Good Example  http://naver.com/post/1  

      RESTful 이란?

      RESTful 이란 REST의 원리를 따르는 시스템을 의미합니다. 하지만 REST를 사용했다 하여 모두가 RESTful 한 것은 아닙니다. 

      REST API의 설계 규칙을 올바르게 지킨 시스템을 RESTful하다 말할 수 있으며 모든 CRUD 기능을 POST로 처리 하는 API 혹은 URI 규칙을 올바르게 지키지 않은 API는 RESTful 하지 못하다고 할 수 있다.

      RESTful의 기준

      RESTful 이란

      • HTTP와 URI 기반으로 자원에 접근할 수 있도록 제공하는 애플리케이션 개발 인터페이스이다. 기본적으로 개발자는 HTTP 메소드와 URI 만으로 인터넷에 자료를 CRUD 할 수 있다.
      • 'REST API'를 제공하는 웹 서비스를 'RESTful' 하다고 할 수 있다.
      • RESTful은 REST를 REST 답게 쓰기 위한 방법으로, 누군가가 공식적으로 발표한 것은 아니다.

      RESTful API 개발 원칙

      a. 자원을 식별할 수 있어야 한다.

      • URL (Uniform Resource Locator) 만으로 내가 어떤 자원을 제어하려고 하는지 알 수 있어야 한다. 자원을 제어하기 위해서, 자원의 위치는 물론 자원의 종류까지 알 수 있어야 한다는 의미이다.
      • Server가 제공하는 정보는 JSON 이나 XML 형태로 HTTP body에 포함되어 전송 시킨다.

      -> URL RESTFul + JSON / XML 형태로 HTTP body 포함 전송한다. 

      b. 행위는 명시적이어야 한다.

      • REST는 아키텍쳐 혹은 방법론과 비슷하다. 따라서 이런 방식을 사용해야 한다고 강제적이지 않다. 기존의 웹 서비스 처럼, GET을 이용해서 UPDATE와 DELETE를 해도 된다.
      • 다만 REST 아키텍쳐에는 부합하지 않으므로 REST를 따른다고 할 수는 없다.

      c. 자기 서술적이어야 한다.

      • 데이터에 대한 메타정보만 가지고도 어떤 종류의 데이터인지, 데이터를 위해서 어떤 어플리케이션을 실행 해야 하는지를 알 수 있어야 한다.
      • 즉, 데이터 처리를 위한 정보를 얻기 위해서, 데이터 원본을 읽어야 한다면 자기 서술적이지 못하다

      d. HATEOS (Hypermedia as the Engine of Application State)

      • 클라이언트 요청에 대해 응답을 할 때, 추가적인 정보를 제공하는 링크를 포함할 수 있어야 한다.
      • REST는 독립적으로 컴포넌트들을 손쉽게 연결하기 위한 목적으로도 사용된다. 따라서 서로 다른 컴포넌트들을 유연하게 연결하기 위해선, 느슨한 연결을 만들어줄 것이 필요하다.
      • 이때 사용되는 것이 바로 링크이다. 서버는 클라이언트 응용 애플리케이션에 하이퍼 링크를 제공한다.
      • 클라이언트는 이 하이퍼 링크를 통해서 전체 네트워크와 연결되며 HATEOAS는 서버가 독립적으로 진화할 수 있도록 서버와 서버, 서버와 클라이언트를 분리 할 수 있게 한다.

      REST의 단점들

      1. REST는 point-to-point 통신모델을 기본으로 한다. 따라서 서버와 클라이언트가 연결을 맺고 상호작용해야하는 어플리케이션의 개발에는 적당하지 않다.
      2. REST는 URI, HTTP 이용한 아키텍처링 방법에 대한 내용만을 담고 있다. 보안과 통신규약 정책 같은 것은 전혀다루지 않는다. 따라서 개발자는 통신과 정책에 대한 설계와 구현을 도맡아서 진행해야 한다.
      3. HTTP에 상당히 의존적이다. REST는 설계 원리이기 때문에 HTTP와는 상관없이 다른 프로토콜에서도 구현할 수 있기는 하지만 자연스러운 개발이 힘들다. 다만 REST를 사용하는 이유가 대부분의 서비스가 웹으로 통합되는 상황이기에 큰 단점이 아니게 되었다.
      4. CRUD 4가지 메소드만 제공한다. 대부분의 일들을 처리할 수 있지만, 4가지 메소드 만으로 처리하기엔 모호한 표현이 있다.
      RESTful 하면 뭐가 좋지?
      self-descriptiveness : RESTful API는 그 자체만으로도 API의 목적(URI만 봐도 이게 뭐하는 URI인지 안다)이 무엇인지 쉽게 알 수 있다. 따라서 API를 RESTful 하게 만들어서 API의 목적이 무엇인지 명확하게 하기 위해 RESTful 함을 지향해야 한다.

      결론

      RESTful 하려면

      • 모든 CRUD기능을 POST로 처리하지 않는다.
      • REST API의 설계 규칙을 올바르게 지킨다.
      • REST의 특징을 따른다.
        • 일관된 인터페이스
        • 무상태성
        • 캐시 기능
        • 서버-클라이언트 구조
        • 자체 표현

      'Back-end > API' 카테고리의 다른 글

      API GateWay  (0) 2022.04.06
      API와 ENDPOINT란?  (0) 2022.04.06

      목차

        돌아가기

        진보 안하는 HTTP

        HTTP/0.9

        - HTTP가 등장한 때는 1990년인데, 이 당시 HTTP가 정식 사양서는 아니었습니다. 이 당시 등장한 HTTP는 1.0 이전이라는 의미에서 HTTP/0.9로 불리고 있습니다.

         

        HTTP/1.0

        - HTTP가 정식 사양으로 공개된 것은 1996년 5월이었습니다. 이 때 HTTP/1.0으로 RFC1945가 발행되었습니다. 초기의 사양이지만 현재에도 아직 많은 서버상에서 가동되고 있습니다.

         

        HTTP/1.1

        - 1997년 1월에 공개된 HTTP/1.1 버전이 현재 가장 많이 사용되는 버전입니다. 

         

        웹 문서 전송 프로토콜로서 등장한 HTTP는 거의 버전 업이 되지 않았습니다. 현재 차세대 HTTP/2.0이 책정되어 있지만 널리 사용되기까지는 아직 시간이 걸릴 것 입니다.
        HTTP가 등장할 당시 텍스트를 전송할 수단이었지만 프로토콜 자체가 워낙 심플해서 여러 가지 응용 방법을 고려해 기능을 계속 추가하였습니다.

        네트워크의 기본은 TCP/IP

        HTTP 를 이해하기 위해선 TCP/IP 프로토콜에 대해 어느 정도 알고 있어야 한다. 인터넷을 포함한 일반적으로 사용하고 있는 네트워크는 TCP/IP 라는 프로토콜에서 움직이고 있습니다. HTTP는 그 중 하나입니다.

        컴퓨터와 네트워크 기기가 상호간에 통신하기 위해서는 서로 같은 방법으로 통신하지 않으면 안됩니다. 따라서 규칙이 필요한데 그것을 프로토콜이라고 부릅니다.

        프로토콜에는 여러가지가 있고 그 중 인터넷과 관련된 프로토콜을 모은 것을 TCP/IP라고 부릅니다. TCP와 IP 프로토콜을 가르켜 TCP/IP라고 부르기도 하지만, IP 프로토콜을 사용한 통신에서 사용되고 있는 프로토콜을 총칭해서 TCP/IP라는 이름이 사용되고 있습니다.

         

        계층으로 관리하는 TCP/IP

        TCP/IP 4 Layer 설명
        TCP/IP가 계층화된 것은 메리트가 있습니다.
        예를들면, 인터넷이 하나의 프로토콜 되어 있다면 어디선가 사양이 변경된다면 전체를 바꿔야하지만, 계층화되어 있으면 사양이 변경되었을 때 전체를 바꾸지 않아도 되고 부분만 변경되면 된다.
        애플리케이션(응용) 계층 응용 계층은 유저에게 제공되는 애플리케이션에서 사용하는 통신의 움직임을 결정하고 있습니다. ex) FTP, DNS 등도 애플리케이션의 한 가지입니다. HTTP도 이 계층에 포함됩니다.
        전송 계층 트랜스포트(전송) 계층은 애플리케이션 계층에 네트워크로 접속되어 있는 2대의 컴퓨터 사이의 데이터 흐름을 제공합니다.
        트랜스포트 계층에서는 서로 다른 성질을 가진 TCP와 UDP 두 가지 프로토콜이 있습니다.
        인터넷(네트워크) 계층 네트워크 계층은 네트워크 상에서 패킷의 이동을 다룹니다. 패킷이란 전송하는 데이터의 최소 단위입니다. 이 계층에서는 어떠한 경로(이른바 절차)을 거쳐 상대의 컴퓨터까지 패킷을 보낼지를 결정합니다.
        인터넷의 경우라면 상대 컴퓨터에 도달하는 동안에 여러 대의 컴퓨터랑 네트워크 기기를 거쳐서 상대방에게 배송 됩니다. 그러한 여러가지 선택지 중 하나의 길을 결정하는 것이 네트워크 계층의 역할이다.
        네트워크(링크, 데이터 링크) 엑세스 네트워크에 접속하는 하드웨어적인 면을 다룹니다. 하드웨어적 측면은 모두 링크 계층의 역할입니다.
        TCP/IP 통신의 흐름 설명
        송신하는 측은 애플리케션 계층에서부터 top-down하고 수신하는측은 bottom-up한다. 
        1) 송신측 클라이언트의 애플리케이션 계층(HTTP)에서 어느 웹 페이지를 보고 싶다라는 HTTP 리퀘스트를 지시
        2) 트랜스포트 계층(TCP)에서는 애플리케이션 계층에서 받은 데이터 (HTTP 메시지)를 통신하기 쉽게 조각내어 안내포트와 포트번호를 붙여 네트워크 계층에 전달
        3) 네트워크 계층(IP)에서는 수신지 MAC 주소를 추가해서 링크 계층에 전달

        HTTP와 관계가 깊은 프로토콜은 IP/TCP/DNS

        배송을 담당하는 IP

        IP(Internet Protocol)는 계층으로 말하자면 네트워크 계층에 해당된다.  'IP'와 'IP주소'를 혼동하는사람이 있는데 'IP'는 프로토콜의 명칭이다. IP의 역할은 개개의 패킷을 상대방에게 전달하는 것이다. 상대방에게 전달하기까지 여러 가지 요소가 필요하다. 그 중에서도 IP주소와 MAC주소라는 요소가 중요하다.

        IP주소와 MAC주소는?
        IP주소는 각 노드에 부여된 주소를 가르킨다. MAC주소는 각 네트워크 카드에 할당된 고유의 주소이다. IP주소와 MAC주소는 결부된다. IP주소는 변경가능하지만, MAC주소는 기본적으로 변경 불가다.

        통신은 ARP를 이용하여 MAC주소에서 한다.

        ip통신은 mac주소에 의존해서 통신을한다. ARP는 주소를 해결하기 위한 프로토콜이며 수신지의 IP주소를 바탕으로 MAC주소를 조사할 수 있다. 

        그 누구도  인터넷 전체를 파악하고 있지는 않다.

        ARP 예시 설명
        목적지 까지 라우터를 통해 찾아가는 송신측(ARP)

        위 그림과 같이 목적지까지 중계를 하는 도중에 컴퓨터와 라우터 등의 네트워크 기기는 목적지에 도착하기 전까지 대략적인 목적지만을 알고 있다. 이 시스템을 라우팅이라고 부르는데 택배 배송과 흡사합니다. 화물을 보내는 사람은 택배 집배소 등에 화물을 가지고 가면 택배를 보낼 수 있는 것만 알고 있으며, 집배소는 화물을 보내는 곳을 보고 어느 지역의 집배소에 보내면 되는지만 알고 있습니다. 

        신뢰성을 담당하는 TCP

        TCP 계층은 트랜스포트 계층에 해당한다. 신뢰성 있는 바이트 스트림 서비스를 제공한다. 

        바이트 스트림

        용량이 큰 데이터를 보내기 쉽게 TCp 세그먼트라고 불리는 단위 패킷으로 작게 분해하여 관리하는 것을 말한다.

        신뢰성 있는 서비스

        상대방에게 보내는 서비스를 의미한다. 상대에게 확실하게 데이터를 보내기 위해서 TCP는 "쓰리웨이 핸드쉐이킹(three way handshaking)이라는 방법을 사용하고 있다. 

        쓰리웨이 핸드쉐이킹(three way handshaking)
        송신측에서는 최초 SYN 플래그로 상대에게 접속함과 동시에 패킷을 보내고, 수신측에서는 SYN/ACK 플래그로 송신측에 접속함과 동시에 패킷을 수신한 사실을 전달한다. 마지막으로 송신측이 'ACK' 플래그를 보내 패킷 교환이 완료됨을 전한다. 만약 중간에 끊어지면 tcp는 그와 동시에 같은 수순으로 패킷을 재전송한다.

        이름 해결을 담당하는 DNS

        DNS(Dmain Name System)는 HTTP와 같이 응용 계층 시스템에서 도메인 이름과 IP주소 이름 확인을 제공합니다. 컴퓨터는 IP주소와는 별도로 호스트 이름과 도메인 이름을 붙일 수 있습니다. 컴퓨터는 IP주소와는 별도로 호스트 이름과 도메인 이름을 붙일 수 있습니다. DNS는 도메인명에서 IP주소를 조사하거나 반대로 IP주소로부터 도메인명을 조사하는 서비스를 제공하고 있습니다. 

         

         

         

         

        전체 흐름도

         

         

         

         

         

         

         

         

         

         

         

         

        'Back-end > NetWork (HTTP)' 카테고리의 다른 글

        [HTTP] 그림으로 배우는 Http NetWork Basic  (0) 2022.01.17
        강좌명 기한 기한 완료여부
        1강 웹과 네트워크의 기본에 대해 알아보자 3/25(금) OK
        2강 간단한 프로토콜 HTTP    
        3강 HTTP 정보는 HTTP 메시지에 있다    
        4강 결과를 전달하는 HTTP 상태 코드    
        5강 HTTP와 연계하는 웹 서버    
        6강 HTTP 헤더    
        7강 웹을 안전하게 하는 HTTPS    
        8강 누가 엑세스하고 있는지를 확인하는 인증    
        9강 HTTP에 기능을 추가한 프로토콜    
        10강 웹 콘텐츠에서 사용하는 기술    
        11강 웹 공격 기술    

        'Back-end > NetWork (HTTP)' 카테고리의 다른 글

        웹과 네트워크의 기본에 대해 알아보자  (0) 2022.01.19

        목차

          캐시(Cache)란?

          Chche
          Chche란 자주 사용하는 데이터나 값을 미리 복사해 놓는 임시장소를 가리킨다. 아래와 같은 저장공간 계층 구조에서 확인할 수 있듯이, 캐시는 저장 공간이 작고 비용이 비싼 대신 빠른 성능을 제공한다.

          Chche를 고려하는 경우

          • 접근 시간에 비해 원래 데이터를 접근하는 시간이 오래 걸리는 경우(서버의 균일한 API 데이터)
          • 반복적으로 동일한 결과를 돌려주는 경우(이미지나 썸네일 등)
          Chche에 데이터를 미리 복사해 놓으면 계산이나 접근 시간 없이 더 빠른 속도로 데이터에 접근 가능하다. 지속적으로 DBMS 혹은 서버에 요청하는 것이 아니라 Memory에 데이터를 저장하였다가 불러다 쓰는 것을 의미한다. DBMS의 부하를 줄이고 성능을 높이기 위해 캐시(Cache)를 사용한다. 원하는 데이터가 캐시에 존재할 경우 해당 데이터를 반환하며, 이러한 상황을 Cache hit라고한다. 반대로 캐시에 원하는 데이터가 없을 시 Cache Miss라고 한다. 

          Local Cache vs Global Cache.

          프링의 PSA 덕분에, 우리는 서비스 환경에 따라 어떤 Cache 전략이던 CacheManager를 구현하고 있기만 하다면 유연하게 우리 코드에 적용할 수 있습니다.

           

          그럼 어떠한 Cache 기술을 적용할지 고려해보아야 되겠죠? Cache 관리 전략을 선택할 때 가장 먼저 고려해야 할 요소는, 캐시 데이터를 저장할 스토리지를 서버가 자체적으로 소유하고 있을지, 외부 서버에 캐시 저장소를 따로 둘 지에 대한 부분입니다. 캐시 저장소를 서버에 두는 방식을 Local Cache, 외부 캐시 저장소를 두는 방식을 Global Cache라고 합니다.

          Local Cache

          데이터 조회 오버헤드가 없다.

          캐시 데이터를 서버 메모리상에 두는 것의 가장 큰 장점은, 무엇보다도 속도가 빠르다는 점입니다. 캐시를 외부 저장소에 저장하면 네트워크 통신을 통해 캐시 저장소에 접근하고, 데이터를 가져오는 과정 등의 오버헤드가 없기 때문에 Local Cache의 데이터 읽기 속도는 현저히 빠릅니다.

          서버 간 데이터 일관성이 깨질 수 있다.

          Local Cache는 단일 서버 인스턴스에 캐시 데이터를 저장하기 때문에, 서버의 인스턴스가 여러 개일 경우 서버 간 캐시 데이터가 일치하지 않아 신뢰성을 떨어뜨릴 수 있습니다.

          서버간 동기화가 어렵고, 동기화 비용이 발생한다.

          캐시 일관성을 유지하기 위해 동기화를 한다고 하더라도, 추가적인 비용이 발생합니다. 더군다나 서버의 개수가 늘어날수록, 자신을 제외한 모든 인스턴스와 동기화 작업을 해야 하기 때문에 비용의 크기는 서버의 개수의 제곱에 비례하여 증가합니다.

          더보기

          그럼에도 불구하고 나는 왜 로컬 캐시를 썼는가?

          금칙어 조회같은 경우 CS에서 특정부분에만 국한되어 사용되며 데이터의 추가, 변경, 삭제가 빈번하지 않다. 따라서 로컬캐시로 관리하는것이 좋다고 판단하여 사용하였다. 또한 금칙어 몇개가 전체 비즈니스에 영향을 크게 주지않는다. 무슨말이냐면 글로벌 캐싱으로 얻는 이점인 서버간 데이터 일관성에 예를들면 금칙어 몇개가 빠르게 동기화되어 일관성이 유지 안된다고하여 전체 비즈니스에 영향을 미치지 않는다 따라서 로컬 캐싱을 사용하였다. 무슨 금전이 오고 가는것도 아니니

          Global Cache

          네트워크 I/O 비용 발생

          Global Cache는 외부 캐시 저장소에 접근하여 데이터를 가져오기 때문에, 이 과정에서 네트워크 I/O가 비용이 발생합니다. 하지만 서버 인스턴스가 추가될 때에도 동일한 비용만을 요구하기 때문에, 서버가 고도화될수록 더 높은 효율을 발휘합니다.

          데이터 일관성을 유지할 수 있다.

          Global Cache는 모든 서버의 인스턴스가 동일한 캐시 저장소에 접근하기 때문에, 데이터의 일관성을 보장할 수 있습니다. 데이터의 일관성이 깨지기 쉬운 분산 서버 환경에 적합한 구조입니다.

           

          Local Cache vs Global Cache, 어떤 기준으로 선택해야 할까?

          Local Cache와 Global Cache의 특성을 고려했을 때, 어떤 기술을 선택해야 할지에 대한 기준은 "데이터의 일관성이 깨져도 비즈니스에 영향을 주지 않는가?"라고 생각합니다.

           

          이를테면, 사용자 정보가 변경되어 프로필에 반영되어야 하는 상황을 가정할 때, 서버 간 동기화가 맞지 않아서 프로필에 반영되는데 시간이 조금 걸린다 하더라도, 전체적인 서비스 운영에 큰 타격을 주지는 않습니다. 금전이 오고가는 문제도 아니고, 프로필의 정보가 조금 늦게 반영된다고 해서 큰 문제가 발생하지 않으니까요. 이러한 경우에는 서버간 동기화 없이 서버 자체적으로 로컬 캐싱을 하는 것도 괜찮은 선택지라고 생각합니다.

           

          하지만, 상품 데이터를 캐싱한다고 했을 때는 상황이 달라집니다. 사용자가 가격을 변경했는데, 그것이 반영되지 않으면 서비스 신뢰를 심각하게 손상하고, 운이 나쁘면 법적 문제로 이어지기도 합니다. 따라서, 이러한 경우에는 동기화가 속도보다 더 중요하며, 그렇기에 동기화가 확실하게 보장되는 Global Cache를 사용하는 것이 좋습니다.

           

          진행 중인 프로젝트에서는 게시글과 댓글에 캐싱을 적용하였고, 둘 다 데이터의 일관성이 중요하다고 판단되어 Global Cache인 Redis를 적용하였습니다. 추후 성능 테스트를 진행하면서, Local Cache를 적용해 성능을 향상할 수 있는 지점을 발견하면, CompositeCacheManager를 사용해 2차 캐시를 구성해보고, 관련한 내용을 새로 포스팅하도록 하겠습니다.

           


          Local-Memory 캐시란?

          스프링에서 제공하는 기본 Cache (SSM)
          spring 3.1버전부터 Spring Application에 캐시를 쉽게 추가할 수 있도록 기능을 제공한다. 유사 트랜잭션을 지원하고, 사용하고 있는 코드(메서드)에 영향을 최소하하면서 일관된 방법으로 캐시를 사용 할 수 있게 된다.

          Spring에서 캐시 추상화는 메소드를 통해 기능을 지원하는데, 메소드가 실행되는 시점에 파라미터에 대한 캐시 존재 여부를 판단하여 없으면 캐시를 등록하고, 캐시가 있으면 메소드를 실행시키지 않고 캐시 데이터를 Return 해주게 된다.
          Spring 캐시 추상화를 지원하기 때문에 개발자는 캐시 로직을 작성하지 않아도 된다. 하지만 캐시를 저장하는 저장소는 직접 설정을 해줘야 한다. Spring에서는 CacheManager라는 Interface를 제공하여 캐시를 구현하도록 되어있다. 
          CacheManager 캐시 저장소
          Spring 에서는 CacheManager 라는 Interface를 제공하여 캐시를 구현하도록 하고 있다. 별다른 의존성을 추가하지 않을 시, Local-Memory에 저장이 가능한 ConcurrentMap 기반인 ConcurrentMapCacheManager가 Bean으로 자동 등록된다.

          캐시 사용하기

          캐시 설정 등록
          @EnableCaching or @Configuration 등록
          캐시 저장
          @Cachealbe을 통해 캐시할 메서드를 지정한다.
          키를 따로 설정하지 않으면 전체 파라미터가 키가 된다.

          @Cacheable

          • value, cacheNames : 캐시이름
          • key : 같은 캐시명을 사용 할 때, 구분되는 구분 값(KeyGenerator와 함께 쓸 수 없음), 별도 지정이 없을 시 파라미터로 key를 지정.
          • keyGenerator: 특정 로직에 의해 cache key를 만들고자 하는 경우 사용. 4.0이후 버전부터 SimpleKeyGenerator를 사용. Custom Key Generrator를 사용하고 싶으면, KeyGenerrator 인터페이스를 별도로 구현
          • cacheManager : 사용할 CacheManager를 지정(EHCacheManger, RadisCacheManager등)
          • cacheResolver: Cache 키에 대한 결과값을 돌려주는 Resolver (Interceptor역할).
          • CacheResolver를 구현하여 Custom하게 처리 할 수도 있음
          • unless: 캐싱이 이루어지지 않는 조건을 설정. 연산 조건이 true 이면 경우에는 캐싱되지 않음. ex) id가 null아 아닌 경우에만 캐싱 (unless = "#id == null") 
          • sync: 캐시 구현체가 Thread safe 하지 않는 경우, 자체적으로 캐시에 동기화를 거는 속성. default는 false
          • condition: SpEL 표현식을 통해 특정 조건에 부합하는 경우에만 캐시 사용. and, or 표현식등을 통해 복수 조건 사용가능. 연산 조건이 true인 경우에만 캐싱

          Global 캐시 적용 과정

          1. bulid, gradle or maven 등록
          //gradle 등록 예시
          implementation 'org.springframework.boot:spring-boot-starter-data-redis'
          2. 캐시 적용을 위한 캐시매니저 등록
          더보기

          CacheConfig 설정 과정

          @RequiredArgsConstructor
          /** @EnableCaching :해당 애플리케이션에서 캐싱을 이용하겠다는 명시를 처리해줘야 한다.
          해당 어노테이션을 적용하게 되면 @Cacheable 라는 어노테이션이 적용된 메서드가 실행될 때 마다 
          AOP의 원리인 후처리 빈에 의해 해당 메소드에 프록시가 적용되어 캐시를 적용하는 부가기능이 추가되어 작동하게 된다.
          */
          @EnableCaching
          @Configuration
          public class CacheConfig {
          
            /** 이전에 Redis를 이용한 세션 스토리지 등록시 사용했던 Lettuce 기반의 Redis client를 
            Bean으로 등록하여사용하고 있다. 
            */
              private final RedisConnectionFactory redisConnectionFactory;  
          
              /**
              CacheProperties : 캐싱이 적용되는 대상마다 캐시의 만료기간을 쉽게 변경할 수 있도록
              yml(또는 properties) 파일에서 종류별로 만료 기간을 Map에 바인딩한다.
              */
              private final CacheProperties cacheProperties; 
          
              private ObjectMapper objectMapper() {
                  ObjectMapper mapper = new ObjectMapper();
                  mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                  mapper.registerModule(new JavaTimeModule());
                  mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
                  return mapper;
              }
          
              /**
              RedisCacheConfiguration : redisCacheManager에 여러가지 옵션을 부여할 수 있는 오브젝트이다.
              여기서는 캐시의 Key/Value를 직렬화-역직렬화 하는 Pair를 설정했다.
              */
              private RedisCacheConfiguration redisCacheDefaultConfiguration() { 
                  RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                      .defaultCacheConfig()
                      .serializeKeysWith(RedisSerializationContext.SerializationPair
                          .fromSerializer(new StringRedisSerializer()))
                      .serializeValuesWith(RedisSerializationContext.SerializationPair
                          .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper())));
                  return redisCacheConfiguration;
              }
          
          
              /**
             CacheProperties에서 바인딩해서 가져온 캐시명과 TTL 값으로  RedisCacheConfiguration을 만들고 
             Map에 넣어 반환한다. 
             Map을 사용하는 이유는 캐시의 만료기간이 다른 여러개의 캐시매니저를 만들게 됨으로써 발생하는 
             성능저하를 방지하기 위해 하나의 캐시매니저에 Map을 이용하여 캐시 이름별 만료기간을 다르게 사용하기 위함이다.
              */
              private Map<String, RedisCacheConfiguration> redisCacheConfigurationMap() { 
                  Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
                  for (Entry<String, Long> cacheNameAndTimeout : cacheProperties.getTtl().entrySet()) {
                      cacheConfigurations
                          .put(cacheNameAndTimeout.getKey(), redisCacheDefaultConfiguration().entryTtl(
                              Duration.ofSeconds(cacheNameAndTimeout.getValue())));
                  }
                  return cacheConfigurations;
              }
          
              /**
              캐시 매니저를 등록한다.  스프링에서 기본적으로 지원하는 캐시 저장소는 JDK의 ConcuurentHashMap이며 
              그 외 캐시 저장소를 사용하기 위해서는 캐시 매니저를 Bean으로 등록해서 사용해야 한다.
          withInitialCacheConfigurations에 캐시의 종류별로 만료기간을 설정한  redisCacheConfigurationMap을 
              */ 
              @Bean
              public CacheManager redisCacheManager() {
                  RedisCacheManager redisCacheManager = RedisCacheManager.RedisCacheManagerBuilder
                      .fromConnectionFactory(redisConnectionFactory)
                      .cacheDefaults(redisCacheDefaultConfiguration())
                      .withInitialCacheConfigurations(redisCacheConfigurationMap()).build();
                  return redisCacheManager;
              }
          }
          3. @Cacheable 등록
          //JPA 호출
          @Cacheable(value = "product", key = "#id")
          public ProductInfoResponse getProductInfo(Long id) {
               return productRepository.findById(id).orElseThrow(() -> new ProductNotFoundException())
                   .toProductInfoResponse();
          }

          @Cacheable 동작 원리

          • @Cacheable은 AOP 기반으로 동작하며...어쩌구
          • 쉽게 말해서 @Cacheable이 등록된 메서드는 DB에서 쿼리문을 날려 데이터를 가져오는 것이 아닌, 캐시 메모리에서 데이터를 반환하는 것이다.
          • 캐시의 value값은 필수이며 key는 선택적이다.
          • 만약 파라미터가 존재하지 않을 경우 key값은 0으로 처리된다. 왠만하면 key값을 명시적으로 사용하는 것을 권장한다.
          4. @CacheEvict
          @CacheEvict(value = "product", key = "#id")
          @Transactional
          public void updateProduct(Long id, SaveRequest updatedProduct) {
              Product savedProduct = productRepository.findById(id)
                 .orElseThrow(ProductNotFoundException::new);
          
               checkDuplicateUpdatedModelNumber(savedProduct.getModelNumber(),
                   updatedProduct.getModelNumber());
          
                savedProduct.update(updatedProduct);
          }

          @CacheEvict은 지정된 key 값에 해당하는 모든 캐시를 삭제하는 어노테이션이다.

          'Back-end > cache' 카테고리의 다른 글

          Redis vs Memcached  (0) 2022.04.06
          Simple Spring Memcached(SSM) ... 작성중  (0) 2022.04.05

          Artifact
          Maven등에서 빌드 결과로 나오는 개발 산출물을 주로 Artifact라고 합니다. 또한 Java외에 기타 다른 다양한 '산출물'을 Artifact라고 부르며, Delivery 및 Deploy를 위해 최종적으로 관리되는 산출물이라고 생각하면 된다. Artifact를 모아서 저장하는 공간을 Library 또는 Artifactory라고 한다.

          Maven-war-plugin

          Maven-war-plugin
          pom.xml의 dependency에 선언된 각종 라이브러리들과 Java class 파일 웹 어플리케이션의 각종 리소스들을 모아서 하나의 Web Application Archive 압축 파일로 만들어 줍니다.
          war:war
          war 형태의 압축 파일로 빌드하는 명령입니다. 압출 파일은 WAS에 의해 압축이 풀리고 파일이 많은 경우에는 압축 해제 시간이 오래 걸릴 수 있습니다.
          war:exploded
          war 압축 형태를 해체한 디렉토리 형태 구조로 빌드하는 명령입니다. 압축 및 해제 과정이 불필요하고 별도의 디렉토리에 원본 소스를 복사하여 만듭니다. 원본 소스를 건드리지 않고 배포를 원하는 경우에 적합합니다.
          war:inplace
          target 디렉토리 뿐만 아니라 다른 디렉토리에 library와 class 파일들이 생성되도록 하는 명령입니다. 기본적으로 src/main/webapp이 지정되어 있습니다.

          각각의 구조


          SNAPSHOT이란?

          SNAPSHOT
          아직 릴리즈 되지 않은 버전을 뜻한다. 실제(릴리즈)버전과 스냅 샷 버전의 차이점은 스냅 샷에 업데이트가 있을 수 있다는 것이다. 즉, 1.0-SNAPSHOT 오늘 다운로드하면 어제 나 내일 다운로드하는 것과 다른 파일이 제공될 수 있다.
          ex) foo-1.0.jar는 maven이 로컬 저장소에서 라이브러리를 찾으면 현재 빌드에 이 라이브러리를 사용합니다.(만약 안정적이지 않다면 setting.xml 또는 pom.xml)을 검색하여 종속성을 검색합니다. 
          (안정적이지 않다 = foo-1.0.SNAPSHOT.jar와 같은 것을 의미한다.)

           

          'Back-end > 젠킨스CICD' 카테고리의 다른 글

          젠킨스 파이프라인 구성(jenkins Pipeline)  (0) 2022.04.05
          메이븐(Maven) - 개념  (0) 2021.01.29

          Maven이란?

          Maven은 자바 프로젝트의 빌드(build)를 자동화 해주는 빌드 툴(Build tool) 이다. 즉, 자바소스를 compile하고 package해서 deploy하는 일을 자동화 해주는 것이다. 

           

          Maven이 참조하는 설정 파일

          Maven 전체를 보기보다 프로그래밍에 직접적인 연관이 있는 두 개 의 설정 파일을 알아보면 된다.

          • settings.xml
            • settings.xml은 maven tool 자체에 관련된 설정을 담당한다. MAVEN_HOME\conf\ 아래에 있는 설정이다. ( MAVEN_HOEM은 환경변수에 설정한 경로) Maven 자체에 설정 값을 바꾸는 일은 일단 잘 없다.
          • pom.xml
            • 하나의 자바 프로젝트에 빌드 툴로 maven을 설정했다면, 프로젝트 최상위 디렉토리에 "pom.xml"이라는 파일이 생성되었을 것이다. 
            • pom.xml은 POM(Project Object Model)을 설정하는 부분으로 프로젝트 내 빌드 옵션을 설정하는 부분이다.
            • 꼭 pom.xml이라는 이름을 가진 파일이 아니라 다른 파일로 지정할 수도 있다. (mvn -f ooo.xml test)
            • 그러나 maven의 원칙으로 다른 개발자들이 헷갈릴 수 있으므로 그냥 pom.xml으로 쓰기를 권장한다.
          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              /*maven의 pom.xml의 모델 버전이다. 형식이 4.0.0 버전이라고 생각하면 된다.*/
              <modelVersion>4.0.0</modelVersion>
              
           	/*groupId 프로젝트를 생성한 조직 또는 그룹명으로 보통, URL의 역순으로 지정한다.*/
              <groupId>com.example</groupId>
              
              /*프로젝트에서 생성되는 기본 아티팩트의 고유 이름이다.*/
              <artifactId>demo</artifactId>
              /*SNAPSHOT이 붙으면 아직 개발단계를 의미한다.*/
              <version>0.0.1-SNAPSHOT</version>
              /*jar, war, ear, pom등 패키지 유형을 나타낸다. */
              <packaging>jar</packaging>
           	/*프로젝트명*/
              <name>demo</name>
              /*프로젝트 설명*/
              <description>Demo project for Spring Boot</description>
            
              <parent>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-parent</artifactId>
                  <version>2.0.5.RELEASE</version>
                  <relativePath/> <!-- lookup parent from repository -->
              </parent>
           
              <properties>
                  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
                  <java.version>1.8</java.version>
              </properties>
           
           	/* 의존성 라이러리 정보 최소한 groupId, artifactId, version 정보가 필요하다 */
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
           
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-devtools</artifactId>
                      /*ompile, runtime, provided, test등이 올 수 있는데 
                      해당 라이브러리가 언제 필요한지 언제 제외되는지를 나태는것으로 따로 검색 해보면 알 수 있다.*/
                      <scope>runtime</scope>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-test</artifactId>
                      <scope>test</scope>
                  </dependency>
              </dependencies>
           
           	/* 빌드정보
              build tool : maven의 핵심인 빌드와 관련된 정보를 설정할 수 있는 곳이다.*/
              <build>
                  <plugins>
                      <plugin>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-maven-plugin</artifactId>
                      </plugin>
                  </plugins>
              </build>
          </project>
           

           

          메이븐의 모든 기능은 플러그인(plug-in)을 기반으로 동작한다.

          플러그인에서 실행할 수 있는 각각의 작업을 골(goal)이라고 하나의 페이즈는 하나의 골과 연결되며, 하나의 플러그인에는 여러개의 골이 있을 수 있다.

           

          * 라이프 사이클

          mvn process-resources : resources:resources의 실행으로 resource 디렉토리에 있는 내용을 target/classes로 복사한다.
          mvn compile : compiler:compile의 실행으로 src/java 밑의 모든 자바 소스를 컴파일해서 target/classes로 복사
          mvn process-testResources, mvn test-compile : 이것은 위의 두 개가 src/java였다면 test/java의 내용을 target/test-classes로 복사. (참고로 test만 mvn test 명령을 내리면 라이프사이클상 원본 소스로 컴파일된다.)
          mvn test : surefire:test의 실행으로 target/test-classes에 있는 테스트케이스의 단위테스트를 진행한다. 결과를 target/surefire-reports에 생성한다.
          mvn package : target디렉토리 하위에 jar, war, ear등 패키지파일을 생성하고 이름은 <build>의 <finalName>의 값을 사용한다 지정되지 않았을 때는 아까 설명한 "artifactId-version.extention" 이름으로 생성
          mvn install : 로컬 저장소로 배포
          mvn deploy : 원격 저장소로 배포
          mvn clean : 빌드 과정에서 생긴 target 디렉토리 내용 삭제
          mvn site : target/site에 문서 사이트 생성
          mvn site-deploy : 문서 사이트를 서버로 배포
          위와 같은 진행 순서로 라이프 사이클이 진행된다. 

          * build 설정 값

          이제 <build>에서 설정할 수 있는 값을 확인해보자.
          <finalName> : 빌드 결과물(ex .jar) 이름 설정
          <resources> : 리소스(각종 설정 파일)의 위치를 지정할 수 있다.

          - <resource> : 없으면 기본으로 "src/main/resources"

          <testResources> : 테스트 리소스의 위치를 지정할 수 있다.

          - <testResource> : 없으면 기본으로 "src/test/resources"

          : 빌드할 때 접근할 저장소의 위치를 지정할 수 있다. 기본적으로 메이븐 중앙 저장소인 http://repo1.maven.org/maven2로 지정되어 있다.

          <outputDirectory> : 컴파일한 결과물 위치 값 지정, 기본 "target/classes"

          <testOutputDirectory> : 테스트 소스를 컴파일한 결과물 위치 값 지정, 기본 "target/test-classes"

          <plugin> : 어떠한 액션 하나를 담당하는 것으로 가장 중요하지만 들어가는 옵션은 제 각각이다. 다행인 것은 플러그인 형식에 대한 것은 안내가 나와있으니 그것을 참고해서 작성하면 된다.

          plugin이 작성되어 있다고 무조건 실행되는 것은 아니다. 명확한 것은 아니지만 따로 실행할 플러그인을 메이븐 명령어로 실행해야 하는 것으로 알고 있다.

          - <executions> : 플러그인 goal과 관련된 실행에 대한 설정

          - <configuration> : 플러그인에서 필요한 설정 값 지정

          apache CXF를 이용한 code generate 플러그인은 아래에서 소개되고 사용한다.

          + Recent posts