도메인이 제일 중요하다. 화면, 인프라, UI등을 모두 제외한 시스템이 구현해야 하는 핵심 비즈니스 업무 영역이다. 그래서 웹이든 뭐든 다 여기를 기반으로 의존해야 하고, 도메인은 어딘가에 의존하지 않고 마음대로 할 수 있어야 잘 짠거임.
멤버 로그인을 구현한건데, 앞에서 배웠던 것들을 하나로 합친 김에 무슨일이 일어난건지 정리해보자.
일단 핵심 기능인 멤버 도메인을 정의하고 그 멤버들을 저장하는 레퍼지토리를 만든다. 이때 멤버에도 제약을 둘 수 있고, 원래 이 레퍼지토리를 데이터베이스 같은 곳에 담겠지만 일단 메모리에 저장한다. 멤버를 저장하고 멤버를 불러오는 코드는 레퍼지토리 클래스가 관리한다.
로그인 폼을 만드는데, 로그인 폼은 로그인 용 서비스 클래스를 만들어 폼과 HTTP 요청을 주고받는데 멤버 객체 상태의 제약으로만 에러를 관리하려면 더 힘들어지기 때문에 로그인 폼 용 클래스를 만들고, 그 로그인 폼 용 클래스 객체에 제약을 걸어서 로그인 시 제약 에러 관리를 한다. 그래서 빈 칸이 있는 로그인 용 제약은 로그인 폼 클래스 객체의 제약에 걸려 에러를 내는것이고, 이 제약들을 통과해서 에러가 나는건, 컨트롤러에서 에러 없이 정상적으로 들어가 레퍼지토리에서 해당 객체를 찾는거고, 여기서 없다면 에러를 직접 추가해서 BindingResult를 통해 내보내는 것이다. 에러가 떴는데도 저게 저장되어 있는 이유는 정상적일 땐 모델을 그대로 통과해서고, 에러가 났을 땐 그 에러 값을 저장해주는 BindingResult의 기능 덕분이다. 비밀번호같은건 html에서 지정한 타입 보고 일부러 저장 안하는듯.
그래서 쿠키가 저장되면, 딱히 뭘 하지 않더라도 해당 주소값의 쿠키가 있으면 앞으로 그 주소값으로 어떤 요청이든 보낼 때 해당 주소값으로 저장된 쿠키들까지 그냥 포함해서 보낸다. 서버는 이걸 받고 처리한다. 즉 이론때 배운 쿠키도 포함해서 보내네 어쩌구 한게 이걸 말한거다.
이제 홈 화면에 갔을 때 쿠키를 가지고 와서 만약 로그인 되어 있다면 모델을 이용해 타임 리프에 해당 로그인 정보를 넣어주고, 같은 홈 경로 url이지만 다른 뷰를 리턴함으로써 다른 결과 화면을 보여준다.
하지만 이런 쿠키 방식은 보안 취약점이 너무 많은데...
3가지 정도가 있다고 한다. 어쨌든 클라이언트에 저장되어 서버로 보내는 것이기 때문에 변조가 가능하다는 것. 특히 그냥 memberId의 숫자만 달랑 넣었으니 기겁할만큼 치명적이다. 저 숫자를 다르게 바꾸면 서버는 별다른거 없이 알았다고 하니 다른 사람 행세를 할 수 있다는 것. 또 저장되어 있는 것이기 때문에 누가 훔쳐갈 수도 있고, 유효기간도 없어서 한번 훔쳐지면 해커가 평생 써먹을 수 있다.
이런 문제들은 세션을 쓰면 다 해결된다. 중요 정보는 서버에 저장하는 방식이다.
어찌됐든 간에 로그인 정보를 클라이언트에서 기억해야 한다. 근데 개인정보는 쿠키가지고 털리면 안된다. 그래서 추적이 진짜 거의 불가능한 uuid를 키로해서 키 값만 쿠키로 저장하고 그 키와 매칭된 중요 정보는 db에서 관리한다. 그리고 이 세션 아이디 값을 통해서만 서로 의사소통 한다. 그리고 여기에 유효기간을 둔다. 이러면 자신에게 저장되어 있는 쿠키 값이 그냥 무작정한 랜덤 값이기 때문에 마음대로 조작할 수 없고, 다른사람이 털어가도 여기 안에 카드 정보나 아이디 같은 중요 정보는 일절 존재하지 않으며 만약 털어서 행세를 한다고 해도 유효기간이 있다. 또는 의심될 경우 서버에서 강제로 제거하면 된다.
조금 헷갈리는데 그림을 번갈아가면서 보면 이해할 수 있다. createSession이 이제 uuid인 sessionId만 헤더에 쿠키로 넣어 클라이언트한테 보낼거고, 이 sessionId를 키 값으로써 무언가의 유의미한 정보를 끄집어낼 수 있는 정보를 서버에만 저장할 거다. 그래서 createSession이 uuid인 sessionId를 만들어 value로는 유의미한 정보에 키 값을 쿠키로써 헤더에 추가하는 과정이고 getSession은 클라이언트에서 쿠키와 함께 요청이 왔을 때 클라이언트의 쿠키에서 sessionId를 꺼내 내가 저장한 키 값들을 찾아 그 정보를 가져오는 것이다. 여기서 세션 만료는 해당 쿠키 세션 키값을 내 서버에서 삭제해서 없어지게끔 구현한 것이다.
이를 테스트할 수 있는데, 실제 http 통신이기 때문에 난감할 순 있지만 우리 스프링에선 Mock으로 가상 요청을 지원해준다.
위 테스트에선 response으로 사용자에게 응답 헤더에 세션 key uuid가 담긴 쿠키를 헤더에 포함시켜서 보내는 흉내다. 모방용 http 요청을 만들고 거기 헤더에 아까 만든 쿠키를 그대로 넣어 사용자가 쿠키를 담아 요청을 보냈다는 걸 흉내낸다. 이걸로 세션을 조회 했을 때 요청에 담긴 쿠키값으로 가져온 값(객체, 여기선 멤버 객체)랑 내가 가지고 있는 객체랑 같은지 확인하고 만료되었을 시 세션 저장소에 삭제되었나도 테스트한다.
엄청 달라진 거 없고 쿠키 대신 세션을 이용하는 것 뿐이다. 정보는 쿠키 값에서 id를 가져와 내 db에서 해당 id에 일치하는 정보를 찾아서 띄워주는게 아니라, 세션에 있는 키 값을 이용해서 내 db를 조회해서 가져온다. 비슷하다고 생각할 수 있지만 중요 정보를 클라이언트가 모르게 한다.
당연히 스프링에서도 세션을 지원한다. 직접 만들었던 것과 똑같고, 시간이 너무 오래 지나면 삭제하는 등의 추가적인 편리한 기능이 있을 뿐이다.
createSession에 true, false 하는건 없다면 create를 할건지 안할건지. 삭제하는건 invalidate() 해주면 된다.
더 편리하게 @ModelAttribute마냥 @SessionAttribute도 지원해준다. 알아서 매핑해서 해당 객체로 맞춰서 하는거
다만 저걸 하면 url 뒤에 이상한 게 붙게된다.
저건 세션 아이디인데, 가끔 쿠키를 지원하지 않는 브라우저가 있어서 저걸로 유지한다. 처음엔 브라우저가 지원 하는지 안하는지 모르기 때문에 같이 보내고 다음에 응답 올 때 쿠키가 들어있으면 지원한다고 판단해서 다음엔 저게 안 붙는다. 처음부터 끄고 싶으면 설정에서 하면 된다.
다음은 세션 타임아웃에 대해 알아볼 건데, 그 전에 기본적으로 무슨 정보가 있나 살펴보자.
보면 maxInactiveInterval이라고 있는데, 기본 설정은 마지막 사용 이후 정해준 시간이 지나면 해당 세션을 메모리에서 삭제한다. 1800이므로 마지막 활동 이후 1800초가 지나면 메모리에서 세션을 삭제한다. 이 값은 글로벌로 설정할 수 있고, 안에서 구체적으로 설정할 수 있다. 물론 구체적인게 더 우선순위가 높음.
실제로 시간이 지나면 비록 쿠키엔 세션 키가 남아있지만, 서버에서는 삭제되서 해당 세션은 존재하지 않는다고 뜬다.
'CS > 김영한 스프링 강의' 카테고리의 다른 글
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션8. 예외 처리와 오류 페이지 (0) | 2023.08.05 |
---|---|
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션7. 로그인 처리2 - 필터, 인터셉터 (0) | 2023.08.05 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션5. 검증2 - Bean Validation (0) | 2023.07.31 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션4. 검증1 - Validation (0) | 2023.07.30 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션3. 메시지, 국제화 (0) | 2023.07.27 |