본문 바로가기

백엔드 개발/SpringMVC

쿠키란?

1. 쿠키가 하는 역할이 무엇일까?

 특정 웹사이트에는 아이디 기억하기란 장치가 있다. 이는 한번 로그인 했으면 다음에 로그인 페이지를 재방문 했을 때, 해당 아이디가 미리 찍혀 있도록 하는 것이다. 

아이디 기억 check -> 쿠키 생성
로그인 후 다시 상단의 log in 클릭
아이디가 기억 되어 있다.

개발자 도구를 통해 쿠키가 뭐 있나 확인해보면, id란 이름으로 쿠키가 들어있는 것을 알 수 있다.

앞에서 배웠다시피, HTTP는 상태 정보를 저장하지 않는다. 따라서 클라이언트는 연속적으로 온 두 요청이 같은 클라이언트에게서 온 것인지, 다른 클라이언트에게서 온 것인지 알 수 없다. 하지만 때때로 서버가 전 클라이언트의 행동을 기억해야만 처리할 수 있는 일이 있다. (ex - 로그인 후에만 할 수 있는 행동 등) 

쿠키는 클라이언트를 식별하는 기술이다. 서버가 쿠키를 이용하면 클라이언트를 구별할 수 있다.

2. 그래서 쿠키란? 

쿠키도 하나의 객체이다. 

쿠키는 이름과 값의 쌍으로 구성된 작은 정보이다. 

이 정보는 오로지 아스키 문자로만 작성되어 있다. (ASCII-문자는 만국공통이기 때문에)

만약 쿠키 안에 한글 정보를 넣어야 한다면, 브라우저에 스트링 쿼리를 보낼 때 처럼 인코딩을 해줘야 한다. 

 

또한 쿠키에 들어가는 곁가지 정보로는 

해당 쿠키의 유효기간, 해당 쿠키가 쓰이는 도메인,경로 주소등이 있다. 

*도메인이란? 

주소의 이름을 말한다. 밑의 예시에 나온 영역까지를 도메인이라 말하고, 이제 wix.com/rigister 나 wix.com/buylist 같이 도메인 안에 들어가서 활동하기 위한 세부 주소를 경로 주소라고 한다.

 

쿠키는 유효기간이 끝나면 자동으로 삭제되도록 설정되어 있다.

유효기간은 상대시간과 절대시간으로 나뉜다. 

상대시간은 쿠키가 만들어진 시점부터 죽을 시간까지 카운트 다운 되는 것이고, 

절대시간은 정확한 년월일 시분초가 적혀 있는 것이다. 

컴퓨터마다 시계가 어긋나있을 수 있으므로, 절대시간과 같이 상대시간도 쿠키 속에 저장된다. 

 

쿠키는 브라우저에 저장되어 있는데, 도메인+경로주소 별로 쿠키가  각각 존재한다. 

클라이언트가 일처리를 요청한 도메인 + 경로 주소와 일치하는 쿠키가 브라우저에 있다면, 

브라우저는 해당 쿠키를 서버에 같이 보낸다. 이와 관련된 자세한 정보는 밑에서 알아보자.

 

3. 쿠키가 서버와 브라우저 사이를 오가는 과정

a. 먼저 쿠키가 있어야 하는 요청이 들어온다. (예에서는 클라이언트의 아이디 기억)

b. 서버에서 쿠키를 생성한다. 그리고 만든 쿠키를 response 객체에 저장한다. 

c. response 객체에 쿠키를 저장하면  톰캣에서 response 객체를 보고 응답 메세지HTTP를 만들어줌. 

d. 응답 메세지 헤더에 쿠키를 만들라는 Set-Cookie 명령과 저장해야할 쿠키 정보도 같이 적힘.

 

e. 그러면 쿠키 정보가 브라우저에 저장되고, 클라이언트가 같은 도메인+경로주소로 요청을 했을 때 이 쿠키도 딸려서 같이 간다.

 

아이디를 기억하는 원리를 설명하기 위해

실생활 예시를 들면, 도서관에 처음 갔을때, 사서가 나한테 도서관ID 카드를 만들어준 것과 같다. 

난 그 다음부터 도서관에 들어갈 때 그 ID 카드를 쓰면 된다.

4. 쿠키 문법

// 1. 쿠키 객체 생성
Cookie cookie = new Cookie("id", "asdf"); // name과 value 쌍
cooike.setMaxAge(60*60*24); // 유효시간 설정 (기본 단위가 초임 여기선 24시간 설정)
response.addCookie(cookie); // response 객체에 담아야 응답HTTP 메세지에 딸려 들어감. 
							// 이거 안 쓰면 말짱 도루묵
// 2. 쿠키 삭제 
Cookie cookie = new Cookie("id",""); //삭제할 쿠키의 name을 쳐야함. value는 쳐도 되고 안 쳐도 됨. 
cookie.setMaxAge(0);				 //유효시간을 0으로 만들어주면, 삭제됨을 이용
response.addCookie(cookie);			 //변경 내용을 갱신 해야함.
// 3. 내용 변경

cookie.setValue(URLEncoder.encode("전수민"));	  //값 변경 - 한글은 인코딩 해줘야함을 기억하자. 
cookie.setDomain("www.fastcampus.co.kr");		//도메인 변경 
cookie.setPath("/ch")							//경로주소 변경
cookie.setMaxAge(60*60*24*7);					//유효기간 변경
response.addCookie(cookie);						//응답에 쿠키 추가
// 4. 다시 서버로 브라우저에 있는 쿠키 읽어오기. 

Cookies[] cookies = request.getCookies();	//request 객체에 톰캣이 넣어준 쿠키정보 가져오기.
						//쿠키가 없으면 null이 뜨기 때문에 null check 필요

for(Cookie cookie : cookies) {
	String name = cookies.getName();
    String value = cookie.getValue();		// 값 읽기
    
    System.out.printf("[cookie]name=%s, value=%s%n", name, value)	//출력
}

5. 코드 리뷰 (HTML/주석달기)

 /*1.CSS */
   <style>
     /* boder-box -> 테두리와 안쪽 여백도 box 너비 계산 시 포함. */
       * { box-sizing:border-box; }
       a { text-decoration: none; }
        form {
            width:400px;
            height:500px;
            display : flex;
            flex-direction: column;
            align-items:center;
            /* 	absolute는 부모 요소중 처음으로 static이 아닌 요소에 대하여 적용
            	top: 50% 는 부모 요소 맨 위에서 50%만큼 떨어지겠다는 소리
            	근데 이게 왼쪽 위 모서리가 기준이라서, 박스 자체가 정 가운데 오진 못함.*/
            position : absolute;
            top:50%;
            left:50%;
            /*그래서 완전히 중앙 맞추기 위해 transform 태그 이용
              translate(수평, 수직): 본 위치를 기준으로 측정
              translate(-50%,-50%)
              본 위치에서 50퍼만큼 왼쪽으로, 50퍼만큼 위로 
              */
            transform: translate(-50%, -50%) ;
            border: 1px solid rgb(89,117,196);
            border-radius: 10px;
        }
        input[type='text'], input[type='password'] {
            width: 300px;
            height: 40px;
            border : 1px solid rgb(89,117,196);
            border-radius:5px;
            padding: 0 10px;
            margin-bottom: 10px;
        }
        button {
            background-color: rgb(89,117,196);
            color : white;
            width:300px;
            height:50px;
            font-size: 17px;
            border : none;
            border-radius: 5px;
            margin : 20px 0 30px 0;
        }
        #title {
            font-size : 50px;
            margin: 40px 0 30px 0;
        }
        #msg {
            height: 30px;
            text-align:center;
            font-size:16px;
            color:red;
            margin-bottom: 20px;
        }
    </style>
<!--2.HTML -->
<body>
<!--form의 마지막 보내기 버튼을 누르면 formCheck란 함수에 해당 입력들 넣어봐라.
	만약 반환 값이 false면 보내지 말고, 반환 값이 true면
	/Login/login 주소로 post요청(데이터에 글쓰기)을 해라.-->
    <form action="<c:url value='/login/login'/>" method="post" onsubmit="return formCheck(this);">
        <h3 id="title">Login</h3>
        <div id="msg">
        <!--test = 만약 ~이면 if 태그 안에 문장 실행
        	not empty ~: ~가 안 비어있으면 -->
	    <c:if test="${not empty param.msg}">
		<i class="fa fa-exclamation-circle"> ${URLDecoder.decode(param.msg)}</i>            
	    </c:if>        
	</div>
		<!-- value는 만약 cookie 값이 있으면 적히도록 -->
        <input type="text" name="id" value="${cookie.id.value }" placeholder="이메일 입력" autofocus>
        <input type="password" name="pwd" placeholder="비밀번호">
        <button>로그인</button>
        <div>
        	<!-- 삼항 연산자로 만약 쿠키가 비어있으면 안적고 있으면 checked 속성 넣기  -->
            <label><input type="checkbox" name="rememberId" ${empty cookie.id.value ? "" : "checked" }> 아이디 기억</label> |
            <a href="">비밀번호 찾기</a> |
            <a href="">회원가입</a>
        </div>
        <script>
        	/* 	자바스크립트 변수 선언
        		var -> 같은 이름의 변수를 계속 재선언해도 에러가 안 남. 
        			   전부 출력해줌.
        			   var name = 'javascript'
        			   var name = 'bathingape'
        			   둘 다 ok
        		let -> 일반 언어들의 변수 선언과 같은 느낌 
        		       재선언은 안되나 재할당(대입)은 된다.
        		       var의 예시 불가.
        		       let name = 'javascript'
        		       name = 'bathingape'
        		       이건 가능. 
        		const > 재선언, 재할당 모두 불가 
        		        한번 초기화 했으면 그대로 가야함.
        		*/
            function formCheck(frm) {
                 let msg ='';
     
                 if(frm.id.value.length==0) {
                     setMessage('id를 입력해주세요.', frm.id);
                     return false;
                 }
     
                 if(frm.pwd.value.length==0) {
                     setMessage('password를 입력해주세요.', frm.pwd);
                     return false;
                 }
                 return true;
            }
     
            function setMessage(msg, element){
            	// msg란 id를 가진 요소를 찾아서 
            	// 그 안의 내용물을 인수로 받은 값으로 바꿔라. 
            	// innerHTML은 해당 태그 요소가 <> <> 사이에 가진 내용물을 바꾸는 것.
                 document.getElementById("msg").innerHTML = ` ${'${msg}'}`;
     
                 //화면 틀었을때 해당 요소가 선택되어져 있도록
                 if(element) {
                     element.select();
                 }
            }
        </script>
    </form>
</body>

6. 코드 리뷰(Java/주석달기)

package com.fastcampus.ch2;

import java.net.URLEncoder;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/login")
public class LoginController {
	
	//조회만 할시엔 로그인 입력창을 보여줘라
	@GetMapping("/login")
	public String loginform() {
		return "loginForm";
	}
	
	// 내용을 쓰고 로그인 요청(POST)를 했을시, 
	@PostMapping("/login")
	public String login(String id, String pwd, boolean rememberId, HttpServletResponse response ) throws Exception {
		
		System.out.println("id=" +id);
		System.out.println("pwd=" +pwd);
		System.out.println("rememberId=" +rememberId);
		
		// 1. id와 pwd를 확인
		if(!loginCheck(id,pwd)) {
			
			String msg=URLEncoder.encode("id 또느 pwd가 일치하지 않습니다.", "utf-8");
			
			// 2-1 일치하지 않으면 loginform 으로 redirect 스트링 쿼리로 
            		// 화면에 보일 메세지도 같이 보냄.
			return "redirect:/login/login?msg="+msg;
			
					
		}
		// 2-2 id와 pwd가 일치하면
		if(rememberId) {// 쿠키를 생성, 응답에 저장
			Cookie cookie = new Cookie("id", id); 
			response.addCookie(cookie);
		} else {// 쿠키를 삭제, 응답에 저장 
			Cookie cookie = new Cookie("id", id); 
			cookie.setMaxAge(0);
			response.addCookie(cookie);
			
		} 
		
		
		

		// 	3. 홈으로 이동	
			return "redirect:/";
	}

	private boolean loginCheck(String id, String pwd) {
		// 원래 매개변수는 null인지 아닌지 검사를 해야하는데 
		// 밑에 처럼 적어놓으면 null check를 해줄 필요가 없음.
		return "asdf".equals(id) && "1234".equals(pwd);
	}

7. 스스로 해보기 (비밀번호도 쿠키가 기억해서 로그인 재 방문 시 찍히게 하기(HTML도 바꾸기), 아이디 쿠키 삭제하기 )

쿠키 객체를 하나 더 만들었다.
쿠키도 들어왔는데

쿠키 2로 받으니 비번은 입력 안된 채로 나왔다. 

그래서 전부

로 했더니 됐다.