ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쿠키와 세션 구현
    Computer기본지식/Computer 2021. 8. 17. 12:37
    반응형

    안녕하세요. 이번에는 간단한 쿠키와 세션을 Java언어로 직접 구현해보는 작업을 가졌습니다.

    Serialize를 공부하다가 보니 쿠키와 세션이 Java의 영속화에 사용된다는 말에 필요한 기술이구나라고 느꼈습니다.

    그래서 간단하게라도 구현해보고 이해해보자라는 생각이 들었습니다. 지금부터 시작해보겠습니다.

     

    1. Session

    세션은 서버에 저장되는 형태로 민감한 정보의 데이터들을 주로 관리해주고

    클라이언트에게는 접근 가능한 임시 키를 발급해주는 형태를 가지고 있습니다.

    그러면 클라이언트가 키를 HTTP요청에 담아서 보내면 그것을 받은 세션은 다시 HTTP에 데이터를 담아 응답해 주는 역할을 합니다.

    간단히 세션에 대해서 구현해봤습니다.

     

    <Controller.java>

    @RestController
    @Slf4j
    public class SessionContoroller {
    
        SessionService sessionService = new SessionService();
    
        @GetMapping("/write")
        public String SessionWrite(HttpServletRequest request) {
            sessionService.SessionWrite(request);
            return "Write";
        }
    
        @GetMapping("/read")
        public String SessionRead(HttpServletRequest request) {
            sessionService.SessionRead(request);
            return "read";
        }
    }

    <Service.java>

    @Service
    @Slf4j
    public class SessionService {
        public void SessionWrite(HttpServletRequest request) {
            HttpSession httpSession = request.getSession();
            httpSession.setAttribute("name","jihuhwan");
        }
    
        public void SessionRead(HttpServletRequest request) {
            HttpSession httpSession = request.getSession();
            Object name = httpSession.getAttribute("name");
            log.info("name = {}", name.toString());
        }
    }

    위의코드는 SessionWrite로 글을 쓰고 다른 페이지로 이동하여 SessionRead로 세션의 값을 읽어들이는 방식의 코드입니다.

    처음에 setAttribute를 통하여 name="jihuhwan"을 지정해놓고 다른 페이지로 이동하여 Server에서 getAttribute를 통해

    값을 가져오도록 구성하였습니다. 

     

    그리고 세션을 보면 다양한 기능들이 있습니다. 그래서 그 기능들을 어떻게 사용하면 좋을지 아래에 코드로 구성하였습니다.

    그리고 위에 보이는 JSESSIONID는 서버측에서 제공해주는 Key입니다. 그래서 Key값을 서버로 전달해주면 서버는

    그 Key값에 맞는 응답을 해줍니다.

    server.session-timeout= # session timeout in seconds
    
    log.info("httpSession.getId = {} ", httpSession.getId());
    log.info("httpSession.getNames = {} ", httpSession.getAttributeNames().asIterator());
    log.info("httpSession.getCreateTime = {}", httpSession.getCreationTime());
    log.info("httpSession.getServletContext = {}", httpSession.getServletContext());
    
    /**
    2021-08-14 09:32:49.858  INFO 25836 --- [nio-8080-exec-1] c.e.S.service.SessionService             : httpSession.getId = 8783A9F4B31C6C47ADE91C49CA7CCEE4 
    2021-08-14 09:32:49.863  INFO 25836 --- [nio-8080-exec-1] c.e.S.service.SessionService             : httpSession.getNames = java.util.Enumeration$1@1d96ecda 
    2021-08-14 09:32:49.863  INFO 25836 --- [nio-8080-exec-1] c.e.S.service.SessionService             : httpSession.getCreateTime = 1628901169446
    2021-08-14 09:32:49.863  INFO 25836 --- [nio-8080-exec-1] c.e.S.service.SessionService             : httpSession.getServletContext = org.apache.catalina.core.ApplicationContextFacade@568593ee
    **/

    이렇게 세션의 기능을 살펴봤습니다. 핵심적인 기능은 서버에서 데이터를 관리해주고 있는 것, 그리고 클라이언트는 Key값으로

    세션에 접근할 수 있다는 것. 두 가지가 세션에서의 핵심적인 기능이라고 생각합니다.

    다음으로는 캐시에 관해서 알아보겠습니다.

     

    2. Cache

    클라이언트 저장소에는 캐시와 쿠키가 존재합니다.

    쿠키같은 경우는 정보를 저장하기 위한 작은저장소라 생각하시면 되고 캐시같은 경우는 웹 페이지의 이미지나 HTML파일 등 크기가 큰 것을 담는 큰 저장소라고 생각하시면 됩니다. 그 중에서도 오늘은 캐시에 대해서 구현을 할 것입니다.

    캐시의 주 목적은 CPU가 빠르게 작동하고 반응하도록 만들고 싶은 목적입니다.

    임시 저장소인 캐시의 동작방법은 두 가지로 크게 나뉩니다.첫 번째로는 "write through"입니다. write through는 캐시와 DB에 둘 다 접근하여 값을 쓰게합니다.결과적으로 속도는 느리지만 DB와 캐시의 일관성(Consistency)이 있다는 특징을 가집니다.두 번째로는 "write back"입니다. write back은 캐시에만 저장하고 나중에 특별한 경우 한 번에 업데이트하는 방식입니다.이 방식은 속도는 빠르지만 다른 Thread에서 DB에 접근하여 값을 꺼낼 경우 일치하지 않은 경우가 발생할 수도 있습니다.

    이렇게 캐시의 기본적인 동작과 역할에 대해서 알아봤습니다. 이제 간단한 캐시를 구현하겠습니다.

    구현에 앞서서 LRU(Least Recently Used)알고리즘을 살펴보겠습니다.

    이름 그대로 최신에 사용한 것들을 차례로 저장하고 오래 된 것들은 버리는 방식으로 동작합니다.

    캐시도 한정된 메모리를 가지고 있기에 모든 것을 받아 저장할 수 없습니다. 그래서 이러한 방식을 사용합니다.

     

    위의 그림을 구현하려면 LinkedList라는 Collection을 사용하면 효율적입니다. LinkedList글은 여기를 참고하시면 됩니다.

    Get(조회), Put(생성), Getall(전체조회) 기능을 구현해야합니다. 그런데 문제가 있습니다.

    LinkedList를 사용하면 Get기능을 구현 할 경우 인덱스를 이용해서 값을 찾아야합니다. 그렇게 되면 처음부터 for문을

    이용하는 것 밖에 없겠죠? 그래서 여기에 HashMap(빠른조회기능)을 더해준 "LinkedHashMap"을 이용합니다.아래의 조회기능의 예시입니다. 확실히 효율적인 프로그램이 될 수 있습니다.

    LinkedList<String> ca = new LinkedList<>();
    ca.add("hello1");
    ca.add("hello2");
    ca.add("hello3");
    ca.add("hello4");
    
    LinkedHashMap<String,String> ba = new LinkedHashMap<>();
    ba.put("hello1","hello1");
    ba.put("hello2","hello2");
    ba.put("hello3","hello3");
    ba.put("hello4","hello4");
    //LinkedList의 조회
    int i = 0;
    while (i < ca.size()) {
    	if (ca.get(i) == "hello3")
    		break ;
    }
    
    //LinkedHashMap의 조회
    ba.get("hello3");

    이를 이용하여 전체적인 코드를 구성하였습니다.

     

    public class CacheController {
        private int size;
        private static LinkedHashMap<String, String> cache = new LinkedHashMap<>();
    
        public CacheController(int size) {
            this.size = size;
        }
    
        public void put(String value) {
            String find = cache.get(value);
            if (find != null)
                cache.remove(find);
            cache.put(value, value);
            if (cache.size() > size)
                cache.remove(cache.get(size));
        }
    
        public String get(String value) {
            return cache.get(value);
        }
    
        public String getAll() {
            return cache.toString();
        }
    }

                            

    Put기능 시 중복되는 값을 가진 경우에는 그 값을 삭제하고 제일 최신에 다시 생성하도록 했습니다.

    그리고 size가 넘어갔을 경우에는 size위치에 있는 데이터를 빼도록 구현하여 위의 그림대로 실행 될 수있게 했습니다.

    아래는 Test예시입니다.

    CacheController cacheController = new CacheController(5);
    String res = "";
    cacheController.put("jihuhwan1");
    res = cacheController.getAll();
    log.info("res = {}" , res); //res = {jihuhwan1=jihuhwan1}
    cacheController.put("jihuhwan2");
    cacheController.put("jihuhwan3");
    res = cacheController.getAll();
    log.info("res = {}" , res); //res = {jihuhwan1=jihuhwan1, jihuhwan2=jihuhwan2, jihuhwan3=jihuhwan3}
    cacheController.put("jihuhwan1");
    res = cacheController.getAll();
    log.info("res = {}" , res); //res = {jihuhwan2=jihuhwan2, jihuhwan3=jihuhwan3, jihuhwan1=jihuhwan1}

    이렇게 LRU방식으로 LinkedHashMap을 이용하여 구현해봤습니다.

    실제 캐시는 어떻게 되있는지 궁금하기도합니다. "Redis"라는 프로그램이 현재 가장 유명한 캐시프로그램으로 알려져있죠.

    다음 규모있는 프로젝트를 할 경우 한 번 사용해봐야겠습니다.

    반응형

    'Computer기본지식 > Computer' 카테고리의 다른 글

    리눅스 메모리 관리  (0) 2021.09.27
    리눅스 파일 시스템  (0) 2021.09.25
    WAS vs 웹 서버  (0) 2021.09.17
    네트워크 보안  (0) 2021.09.15
    Process와 Thread  (0) 2021.09.14

    댓글

Designed by Tistory.