슈퍼코딩/주특기(JAVA)

2024.06.06(목) 슈퍼코딩 신입연수원 10주차 Day 4 후기 - Cache, Cookie & Session & JWT

곰돌이볼 2024. 6. 6. 22:56
  • 강의
    • 109강(HTTP 캐싱과 스프링부트 캐싱) ~ 112강((Spring Security v2)

캐싱


  • 파라토의 법칙 : 상위 20%가 전체의 80%에 영향을 미치는 현상
  • 파레토 법치과 cache 활용
    • 20%의 쿼리가 사용되는 쿼리의 80% 사용됨
  • Cache
    • 데이터의 원래 소스보다 더 효율적으로 엑세스할 수 있는 임시 저장소
    • 웹 애플리케이션에서 사용
      • 1) 클라이언트측 HTTP 캐시 사용 → HTTP Cache
      • 2) 서버측 Spring 캐시 사용 → Spring Cache
  • HTTP Cache(클라이언트측에서 사용)
    • HTTP 클라이언트의 요청에 대한 응답값 임시 저장소
    • Cache Validation을 서버에 보내서 캐시 유효성 확인이 필요
    • HTTP Response Header
      • Cache-Control : 어떤 방식으로 유지할지 설정
      • Expire : 캐시 응답 만료 시점
      • X-Cache : Cache Hit 해당 요청값이 캐시로 응답
      • X-Cache-Location : 해당 Cache가 어디서 저장되었는지 확인(저장소 : Disk 또는 Memory Cache)

Cache Validation

  • 캐시값 유효성 검증
  • 방식 1) If-Modified-Since : 캐시를 얻을 시점 이후로 변경한 사항이 있는지 검증 하는 방식
    • 문제점 : 나라마다 다른 시간을 사용할 경우 문제가 발생할 수 있음
  • 방식 2) E-tag : 할당된 캐시의 ID에 대해서 변경사항이 있는지 검증하는 방식
    • 클라이언트가 If-Noe-Match 값(캐시 ID값) 변경 사항이 없으면 HTTP/1.1 304 Not modified 보냄
    • 아쉬운 점
      • 계속 동작되는 SQL문
      • 큰 서버 속도 변화 X
  • E-tag를 이용한 코드 실습
    • 첫번째 클라이언트의 response Headers에 Etag 가져옴 → 다음 클라이어트 요청 시 If-Noe-Match 에 Etag값을 넣어서 보내면 E-tag 캐시 사용 가능
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;

@Configuration
public class EtagWebConfig {

    @Bean
    public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter(){
        FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new ShallowEtagHeaderFilter());
        filterRegistrationBean.addUrlPatterns("/api/*"); // API 패턴 설정
        return filterRegistrationBean;
    }
}

 

Spring Cache

  • Spring Cache를 사용해서 같은 DB의 질의인 경우에는 RDB 질의 X
  • 사용
    • 캐시값 설정하기 : @Cacheable
      • key = 키값, value = 메서드의 반환값(캐시값)
      • key 값 : #root.methodName, #ids
      • service layer에서 많이 사용
    • 캐기값 초기화 : @CacheEvict
      • value = 초기화할 캐시값, allEntries = true
    • 종속성 추가
      • // Cache
        implementation 'org.springframework.boot:spring-boot-starter-cache'
    • 캐시 사용
      • Main 메서드에 @EnableCache 추가

HTTP 쿠키, 세션, 토큰


  • HTTP 특징 : 무상태성(Stateless) → 사용자 인증 문제 발생
    • 해결방법
      • 1) 쿠키 & 세션 방식 : 서버가 사용자 정보를 가지고 있는 경우
      • 2) 토큰 방식 : 클라이언트측에서 사용자 정보를 가지고 있는 경우

 Cookie 

  • 브라우저에 저장되는 작은 정보 조각
  • 클라이언트에서 사용자 정보를 저장하고 있는 방식
  • 보안 이슈 발생
    • 쿠키 요구 화면 필요
    • 다크 모드 여부, 폰트 정보 등 가벼운 정보 저장

Session

  • 서버에서 사용자 정보를 저장하고 있는 방식
  • 사용자 인증을 할 경우, 서버는 사용자에 대한 정보를 저장하고 사용자에 대한 Session키를 발급해줌 → 클라이언트가 세션키를 쿠키에 저장해서 사용, 사용자 관련 요청 필요 시 세션키를 서버 요청값에 포함시켜서 요청하기
  • 실습 코드
    • set-session API를 통해서 세션을 생성하고, 클라이언트는 세션값을 coockie에 저장해서 사용
package com.github.supercodingspring.web.controller.sample;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/api")
public class SessionTokenSampleController {

	// 세션 생성
    @GetMapping("/set-session")
    public String setSession(HttpSession session){
        session.setAttribute("user", "조인성");
        session.setAttribute("gender", "남자");
        session.setAttribute("job", "배우");
        return "Session Set successfully";
    }

    @GetMapping("/set-session2")
    public String setSession2(HttpSession session){
        session.setAttribute("user", "송혜교");
        session.setAttribute("gender", "여자");
        session.setAttribute("job", "배우");
        return "Session Set successfully";
    }

    @GetMapping("/get-session")
    public String getSession(HttpSession session){
        String user = (String) session.getAttribute("user");
        String gender = (String) session.getAttribute("gender");
        String job = (String) session.getAttribute("job");
        return String.format("안녕하세요, 직업: %s 성별: %s인 %s 입니다.", job, gender, user);
    }


}

 

 

JWT

  • Json Web Token
  • Json 포맷을 이용해서 사용자 속성을 저장하는 claim 기반의 web token 방식
  • 구조
    • Header
      • alg : 암호화 알고리즘
      • typ : JWT
    • Payload
      • Claim 모음의 사용자 속성(공개 claim, 비공개 claim)
      • 등록 claim : sub, exp와 같은 메타 정보
    • Signature
      • 유효성 검증과 암호화 코드
      • 알고리즘 hash값
  • 사용
    • implementation 'io.jsonwebtoken:jjwt:0.9.1'
  • 실습 예제
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/api")
public class SessionTokenSampleController {
    @GetMapping("/generate-token")
    public String generateToken(HttpServletResponse httpServletResponse){
        String jwt = Jwts.builder()
                .setSubject("token1")
                .claim("user", "조인성")
                .claim("gender", "남자")
                .claim("job", "배우")
                .compact();
        httpServletResponse.addHeader("Token", jwt);
        return "JWT set Successfully";
    }

    @GetMapping("/generate-token2")
    public String generateToken2(HttpServletResponse httpServletResponse){
        String jwt = Jwts.builder()
                         .setSubject("token1")
                         .claim("user", "송혜교")
                         .claim("gender", "여자")
                         .claim("job", "배우")
                         .compact();
        httpServletResponse.addHeader("Token", jwt);
        return "JWT set Successfully";
    }
    @GetMapping("/show-token")
    public String showToken(@RequestHeader("Token") String token){
        Claims claims = Jwts.parser()
                .parseClaimsJwt(token)
                .getBody();

        String user = (String) claims.get("user");
        String gender = (String) claims.get("gender");
        String job = (String) claims.get("job");

        return String.format("안녕하세요, 직업: %s 성별: %s인 %s 입니다.", job, gender, user);
    }
}

 

 

JWT  vs  Cookie&Session

  • JWT
    • 장점 : 상태없음, 분산 시스템 유리
    • 단점 : 취약한 보안, JWT 크기 증가
    • => 다양한 플랫폼 운영 가능
  • 쿠키 & 세션
    • 장점 : 보안 유리, 유저 관리 용이
    • 단점 : 상태유지, 서버 부담 큼
    • => 서버 측에서 사용자 상태 관리