+) 22. 08. 10. day51 🌕 에 정리 완료!
+) 22. 08. 09. 추가!!
HTTP는 Connectionless와 Stateless의 특징을 가지고 있다.
이러한 HTTP의 특성으로 인해, HTTP 프로토콜에서 상태를 유지하기 위한 방법으로 쿠키와 세션이 등장하였다.
쿠키는 클라이언트의 브라우저에 저장되는 데이터를 말한다.
쿠키의 구성 요소
동작 방식
브라우저에서 웹 서버로 Request를 보내면 서버에서는 그 Request를 분석하고 처리한 뒤 결과물(Response)을 돌려준다.
이 때 서버는 Response의 헤더 안에 쿠키 정보(Set-Cookie)를 추가해서 보내준다.
클라이언트는 Response를 받고 브라우저 안에 쿠키를 저장할 수 있는 공간에 쿠키 값을 저장해둔다. 이 로컬에 저장된 쿠키는 결국 브라우저가 사용하기 위해 저장하는 것이다.
이렇게 저장된 쿠키는 나중에 서버로 요청을 할 때마다 해당 서버가 발급해준 쿠키를 무조건 포함해서 보내게 되어있다. 따라서 서버에서는 그 요청 안에 있는 쿠키 값을 꺼내, 쿠키 안에 들어있는 데이터가 어떤 의미를 갖고 있는지 알 수 있다. 이를 통해 필요한 것(쇼핑몰 비회원 장바구니 목록, ID 기억하기 등)을 처리할 수 있게 한다.
정리
웹 클라이언트(브라우저)에 저장해놓고 웹 서버로 Request할 때마다 쿠키를 포함하여 전송한다.
웹 서비스를 이용하는 동안 유지할 데이터를 저장하는 용도로 쓰인다. 단, 공개되어도 괜찮은 정보만 저장해놓는다.
유효 시간 동안 브라우저에서 유지된다.
세션은 서버에서 생성되는 기본 객체를 말한다.
동작 방식
클라이언트가 웹 브라우저에서 서버로 Request를 보내면, 서버는 이 브라우저가 누구인지 식별을 한다.
이 때 만약 최초로 서버에 요청을 했다면 서버는 이 브라우저에 대한 정보가 없어 누군지 모를 것이다. 그래서 서버는 이 브라우저를 식별할 수 있도록 세션이라는 기본 객체를 만든다. 만들어진 세션 객체에는 세션 ID 값이 들어있다.
서버는 쿠키에 이 세션 값을 담아 Response를 한다.
브라우저는 서버가 보낸 쿠키를 자신의 로컬에 저장한다.
이렇게 저장이 된 쿠키는 해당 서버로 요청을 할 때마다 같이 포함해서 보내므로, 서버는 해당 요청 안에 담긴 쿠키 정보 중 세션 정보(세션 ID)를 통해 어떤 브라우저인지 식별할 수 있게 된다.
세션은 브라우저마다 하나씩 만들어진다. 이를 활용해서 로그인과 관련된 중요한 정보, 즉, 클라이언트에 저장하면 위험한 정보들을 세션에 저장해 둘 수 있다.
세션은 브라우저를 종료하거나 서버 쪽에서 session.invalidate() 호출 시 사라진다.
정리
웹 서버에서 클라이언트(브라우저)를 식별하기 위해 사용한다.
세션 객체는 웹 서버에 생성된 객체이므로 중요한 정보를 저장하기 좋다. (클라이언트는 세션에 어떤 값이 저장되어 있는지 알 수 없다.)
웹 클라이언트(브라우저)에서 종료하거나, 서버에서 session.invalidate() 시 세션 객체가 제거된다.
참고 자료
chrisjune, “[WEB] 쿠키, 세션이란?”, https://chrisjune-13837.medium.com/web-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98%EC%9D%B4%EB%9E%80-aa6bcb327582
Doy's Development Blog, “쿠키(Cookie)와 세션(Session)”, https://doooyeon.github.io/2018/09/10/cookie-and-session.html
IT핥기, “[JSP] Cookie(쿠키) 이해하기”, https://youtu.be/a2tvTi9qgaE
IT핥기, “[JSP] Session(세션) 이해하기”, https://youtu.be/VrWK1VPW5Qg
함께 보면 좋은 자료
우아한Tech, “[10분 테코톡] 🦄 디토의 웹스토리지 & 쿠키”, https://youtu.be/-4ZsGy1LOiE
노마드 코더 Nomad Coders, “세션 vs 토큰 vs 쿠키? 기초개념 잡아드림. 10분 순삭!”, https://youtu.be/tosLBcAX1vk
+) 22. 08. 05. 추가!!!!!
나 이번에 좀 잘한 것 같다...
랜덤한 정수를 생성하는 클래스
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RandomNumber {
private final static Logger log = LoggerFactory.getLogger(RandomNumber.class);
private final Set<Integer> randomNumber;
public RandomNumber() {
randomNumber = new HashSet<>();
}
public List<Integer> createRandomNumber() {
while(randomNumber.size() < 3) {
randomNumber.add((int) (Math.random() * 9 + 1));
}
log.info("랜덤값 = {}", randomNumber);
return new ArrayList<>(randomNumber);
}
}
사용자에게 값을 입력 받는 클래스
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Input {
public String input() {
System.out.println("\n1~9 사이의 숫자 3개를 입력해 주세요.");
return new Scanner(System.in).nextLine();
}
public List<Integer> toNumbers(String str) {
List<Integer> numbers = Pattern.compile("").splitAsStream(str).map(Integer::parseInt).collect(Collectors.toList());
validateForNumberLength(numbers);
validateForDuplication(numbers);
return numbers;
}
public void validateForNumberLength(List<Integer> numbers) {
if(numbers.size() > 3 || numbers.size() < 3 || numbers.contains(0)) {
throw new RuntimeException("1~9 사이의 3자리 숫자를 입력해 주세요.");
}
}
public void validateForDuplication(List<Integer> numbers) {
int count = (int) numbers.stream().distinct().count();
if (count < 3) {
throw new RuntimeException("중복값이 있습니다.");
}
}
}
input()
사용자에게 값을 입력 받는다.
toNumbers()
입력 받은 값을 한 글자씩 분리하여 리스트에 넣고 정수형으로 반환한다.
validateForNumberLength()
1~9까지의 수인지, 3자리 수가 맞는지 검사한다.
validateForDuplication()
중복값이 있는지 검사한다.
볼인지 스트라이크인지 확인하는 인터페이스
import java.util.List;
public interface Baseball {
int compare(List<Integer> randomNumber, List<Integer> userNumber);
}
Ball인지 확인하는 클래스
import java.util.List;
import java.util.stream.IntStream;
public class Ball implements Baseball {
private final int ball;
public Ball() {
this.ball = 0;
}
@Override
public int compare(List<Integer> randomNumber, List<Integer> userNumber) {
return (int) IntStream.range(0, 3).filter(n -> randomNumber.contains(userNumber.get(n))).count();
}
}
Strike인지 확인하는 클래스
import java.util.List;
import java.util.stream.IntStream;
public class Strike implements Baseball {
private final int strike;
public Strike() {
this.strike = 0;
}
@Override
public int compare(List<Integer> randomNumber, List<Integer> userNumber) {
return (int) IntStream.range(0, 3).filter(n -> randomNumber.get(n).equals(userNumber.get(n))).count();
}
}
결과를 출력하는 클래스
public class Output {
public void output(int ball, int strike) {
if(strike == 3) {
System.out.println("BASEBALL WIN~!");
return;
}
System.out.println(ball + " " + "ball / " + strike + " strike");
}
public void printPlayBaseballGame() {
System.out.println("Play Baseball Game");
}
}
output()
strike와 ball 값을 받아와, 만약 strike가 3이면 답을 맞힌 것이므로 BASEBALL WIN~!을 출력한다.
정답을 맞히지 못했다면 ball, strike 여부를 알려준다.
printPlayBaseballGame()
"Play Baseball Game"을 출력한다.
실행하는 클래스
import java.util.List;
public class Main {
public static void main(String[] args) {
Input input = new Input();
Output output = new Output();
RandomNumber randomNumber = new RandomNumber();
List<Integer> randomNumbers = randomNumber.createRandomNumber();
Ball ball = new Ball();
Strike strike = new Strike();
int totalStrike = 0;
int totalBall = 0;
output.printPlayBaseballGame();
while(totalStrike != 3) {
List<Integer> userNumbers = input.toNumbers(input.input());
totalStrike = strike.compare(randomNumbers, userNumbers);
totalBall = ball.compare(randomNumbers, userNumbers) - totalStrike;
output.output(totalBall, totalStrike);
}
}
}