https://sundaland.tistory.com/194
[ ▶ @SessionAttributes / @ModelAttribute 란? ]
웹 서비스 개발 시, 클라이언트와 서버 저장소의 이해는 필수적이다. 클라이언트 저장소는 로컬 스토리지, 세션 스토리지, 쿠키를 포함하며, 서버 저장소는 세션, 데이터베이스, 캐시를 포함한다. 프론트엔드와 백엔드 개발자 간의 세션에 관한 혼란을 방지하기 위해 이들의 구분이 중요하다.
HTTP는 상태를 저장하지 않는 무상태 프로토콜이며, 각 요청은 독립적으로 처리된다. 이러한 특성은 웹 서비스에서 사용자의 상태를 유지하는 데 제약을 주지만, 실제 웹 서비스는 로그인 상태 유지나 장바구니 정보 보존과 같이 클라이언트 상태를 유지해야한다. 이를 위해 세션과 쿠키를 사용하여 클라이언트의 상태를 서버가 기억할 수 있게 한다.
세션은 서버 메모리, 데이터베이스, 또는 캐시 서버 같은 다양한 기술로 구성될 수 있으며, 사용자 정보, 권한 등을 저장한다.
클라이언트의 첫 요청 시 서버는 세션 저장 공간을 생성하고, 세션 ID를 발급하여 클라이언트에게 전달한다.
쿠키는 서버가 클라이언트에 세션 ID를 저장하도록 하는 메커니즘이다.
서버는 HTTP 헤더를 통해 쿠키 정보를 클라이언트에 전달하고, 브라우저는 이를 자동으로 저장하여 동일 도메인의 서버 요청 시 함께 전송한다.
쿠키는 키-값 형태로 저장되며, 이름, 값, 유효 시간, 도메인, 경로 정보를 포함합니다. 쿠키와 세션을 비교하는 것은 적절하지 않다. 쿠키는 클라이언트 측에서, 세션은 서버 측에서 사용되며, 각각의 역할과 목적이 다르다. 개발자는 이 두 기술을 조합하여 사용자에게 효율적인 서비스를 제공해야 한다.
[ ▷ HTTP 세션 ]
HTTP 세션(HTTP Session)은 웹 서버와 클라이언트(보통 웹 브라우저) 간의 상태(state)를 유지하기 위한 방법이다. HTTP 프로토콜 자체는 상태를 유지하지 않는(stateless) 프로토콜이기 때문에, 각각의 요청과 응답은 독립적으로 처리된다. 이러한 특성 때문에, 사용자가 웹 사이트를 탐색할 때 이전의 활동(예: 로그인 상태, 장바구니 내용, 언어 설정 등)을 기억하기 위한 메커니즘이 필요하다. 이를 위해 HTTP 세션이 사용된다.
- 세션 생성: 사용자가 웹 사이트에 처음 접속할 때, 서버는 고유한 세션 ID를 생성한다. 이 ID는 서버에 저장되며, 사용자의 브라우저에도 쿠키(cookie) 형태로 전송되어 저장된다.
- 세션 ID 이용: 이후 사용자가 동일한 웹 사이트에 요청을 보낼 때마다 브라우저는 이 세션 ID를 포함하여 요청을 전송한다. 서버는 이 ID를 통해 해당 사용자의 세션을 식별하고, 사용자의 상태 정보를 유지할 수 있다.
- 세션 데이터 저장: 서버는 세션 ID에 연결된 데이터(예: 사용자 프로필, 설정, 장바구니 내용 등)를 메모리, 데이터베이스, 파일 시스템 등에 저장할 수 있다. 이를 통해 사용자가 웹 사이트를 탐색하는 동안 필요한 정보를 유지할 수 있다.
- 세션 만료: 세션은 일정 시간 동안만 유효하며, 설정된 시간이 지나면 자동으로 만료된다. 사용자가 로그아웃하거나 오랫동안 활동하지 않으면 세션이 종료될 수 있다.
HTTP 세션은 사용자 인증, 장바구니 데이터 유지, 사용자 선호 설정 관리 등 다양한 용도로 활용된다.
이를 통해 상태를 유지하지 않는 HTTP 프로토콜 상에서도 사용자에게 연속적이고 일관된 경험을 제공할 수 있다.
[ ▷ Browser Storage ]
서버 측 저장소와 유사한 개념을 바탕으로 운영되지만, 브라우저 저장소 혹은 클라이언트 측 저장소는 다양한 사용 사례를 가지고 있다. 이는 클라이언트(즉, 사용자의 컴퓨터)에 데이터를 저장할 수 있게 해주는 JavaScript API로 구성되어 있으며, 필요할 때 나중에 다시 검색될 수 있다.
쿠키, 로컬 스토리지, 세션 스토리지는 브라우저에 데이터를 로컬로 저장하기 위해 가장 자주 사용되는 세 가지 방법이다. 이 세 가지 모두 사용자의 브라우저에 저장된다는 점이 주요 공통점이다. 이는 사용자의 데이터가 크롬에 저장되어 있다면, 파이어폭스와 같은 다른 브라우저를 통해 접근할 수 없음을 의미한다.
[ ▷ Web Storage ]
웹 저장소는 클라이언트 측, 즉 사용자의 브라우저에 데이터를 저장하는 메커니즘을 말한다. 이는 HTML5 명세의 일부로 도입되었으며, 로컬 스토리지(Local Storage)와 세션 스토리지(Session Storage) 두 가지 주요 형태를 포함한다.
- 로컬 스토리지(Local Storage): 로컬 스토리지는 웹 애플리케이션에서 사용할 수 있는 데이터를 사용자의 브라우저에 영구적으로 저장할 수 있게 해준다. 즉, 브라우저를 닫았다가 다시 열어도 데이터가 유지된다. 데이터에 만료 기간이 설정되어 있지 않다.
- 세션 스토리지(Session Storage): 세션 스토리지는 사용자의 브라우저 세션이 유지되는 동안에만 데이터를 저장한다. 사용자가 브라우저 탭을 닫으면 저장된 데이터는 사라진다.
반면, 세션 저장소(Session Storage)는 서버 측에서 관리되며, 사용자가 웹 사이트에 방문하는 동안 서버가 사용자의 상태를 유지하기 위해 사용한다. 서버 측 세션 저장소는 사용자의 세션 ID와 연관된 데이터를 서버에 저장하며, 이 세션 ID는 보통 쿠키를 통해 클라이언트와 공유된다.
따라서 웹 저장소는 클라이언트 측에서 데이터를 저장하는 메커니즘으로, 서버 측에서 운용하는 세션 저장소와는 별개의 개념이다.
[ ▷ Local Storage ]
클라이언트 컴퓨터의 웹 브라우저에서 로컬 스토리지 웹 저장 기술을 사용하여 데이터를 키/값 쌍으로 저장할 수 있다. 사용자가 브라우저에서 명시적으로 삭제하지 않는 한, 데이터는 영구적으로 로컬 스토리지에 보관된다. 사용자가 창이나 탭을 닫더라도 데이터는 유효하게 유지된다. 대신, 정보는 메모리가 지워질 때까지 브라우저의 메모리에 남아 있다.
브라우저에서 로컬 스토리지 정보에 접근할 수 있는 것은 자바스크립트와 HTML5뿐이다. 사용자는 브라우저의 캐시와 데이터를 청소하여 모든 로컬 스토리지 데이터를 완전히 삭제할 수도 있다.
- 로컬 스토리지에 데이터를 설정하기 위해 setItem() 메소드를 사용한다. 이 메소드의 파라미터는 키와 값이다. 이 메소드를 사용하여 키와 함께 값을 저장할 수 있다. localStorage.setItem(key, value);
- 로컬 스토리지에 보관된 데이터에 접근하기 위해 getItem() 메소드를 사용할 수 있다. 이 절차의 유일한 파라미터는 검색하려는 값의 키이다. localStorage.getItem(key);
- 키와 함께 메모리에 보관된 데이터를 삭제하기 위해 removeItem() 메소드를 사용할 수 있다. localStorage.removeItem(key);
- 로컬 스토리지에 보관된 모든 데이터를 지우기 위해 clear() 메소드를 사용할 수 있다.
◎ 장점
- 로컬 스토리지에 보관된 데이터는 만료 날짜가 없다.
- 저장 한도는 대략 10MB이다.
- 로컷 스토리지의 데이터는 서버로 전송되지 않는다.
◎ 단점
- 로컬 스토리지 데이터가 평문으로 되어 있어 보안에 적합하지 않다.
- 데이터 타입이 문자열로 제한되어 있어 직렬화가 필요하다.
- 서버 측이 아닌 클라이언트 측에서만 데이터를 읽을 수 있다.
[ ▷ Session Storage ]
로컬 스토리지와 세션 스토리지는 매우 유사하다. 그러나 주요 차이점은 정보가 브라우저에 얼마나 오래 남야있느냐에 있다. 현재 탭이나 세션이 활성 상태일 때까지 이다. 세션 스토리지에 저장된 데이터는 탭을 닫거나 세션을 종료할 때 삭제된다. 로컬 스토리지와 마찬가지로 setItem() 및 getItem() 메서드를 사용하여 세션 데이터를 설정하고 가져올 수 있다.
[ ▷ Cookies ]
HTML5가 도입되기 전에, 쿠키는 유일한 옵션이었다. 따라서 쿠키를 통해 클라이언트 기기에 데이터를 저장하는 것은 구식 방법이다.
쿠키는 웹사이트 방문자에게 맞춤형 경험을 제공하기 위해 클라이언트 측 데이터를 저장하는 데 도움을 준다. 쿠키는 요청과 함께 서버로 전송되고 응답에서 클라이언트로 다시 반환되므로, 서버와 클라이언트는 각 요청과 함께 쿠키 데이터를 교환한다. 서버는 쿠키 데이터를 사용하여 사용자에게 맞춤형 콘텐츠를 제공할 수 있다.
웹 저장소처럼 자바스크립트를 사용하여 쿠키를 생성, 변경 또는 읽을 수 있다. document.cookie. 크로스 사이트 스크립팅과 같은 몇 가지 보안 취약점을 완화하기 위해, HTTPOnly 쿠키 플래그를 사용하여 자바스크립트에서 쿠키 접근을 제한할 수 있다 (쿠키는 서버에서만 접근할 수 있다).
△ 세션 쿠키
세션 쿠키는 Expires 또는 Max-Age 속성이 없기 때문에 브라우저가 닫힐 때 삭제된다.
△ 지속 쿠키
지속 쿠키는 Expires 또는 Max-Age 속성이 지정된다. 이 쿠키들은 브라우저를 종료할 때가 아니라 설정된 날짜(Expires) 또는 시간(Max-Age)에 만료된다.
[ ▷ 접근성 ]
접근성 측면에서, 로컬 스토리지는 웹사이트를 위해 열린 모든 브라우저 창이나 탭에서 접근할 수 있다. 그러나 세션 스토리지는 특정 세션에 연결되어 있고 각 탭이 자체 세션을 가지고 있기 때문에, 세션 스토리지를 위해 설정된 데이터는 현재 열린 탭에서만 접근할 수 있다.
마지막으로, 쿠키는 로컬 스토리지와 유사하게 모든 창이나 탭에서 접근할 수 있다. 서버에서도 쿠키에 접근할 수 있다. 모든 쿠키는 백엔드 서버로의 각 요청과 함께 전송된다. 결과적으로, 인증 작업을 포함한 작업에도 사용된다.
[ ▷ Differences ]
Local Storage | Session Storage | Cookies | |
Capacity | 10MB | 5MB | 4KB |
Browsers | HTML 5 | HTML 5 | HTML 5 / HTML 4 |
Accessibility | Any Window | Same Tab | Any Window |
Expiration | Never | On Tab Close | Manually Set |
Browser Support | Very High | Very High | Very High |
Supported Data Types | String Only | String Only | String Only |
Auto-expire Option | No | Yes | Yes |
Storage Location | Browser Only | Browser Only | Browser and Server |
Editable and Blockable by Users | Yes | Yes | Yes |
SSL Support | No | No | Yes |
[ ▷ Session Storage of Servlet Container ]
서블릿 컨테이너는 세션 저장소를 만들고 관리하는 주체이다. 서블릿 컨테이너는 웹 애플리케이션을 실행하는 서버의 일부로, 서블릿과 JSP(JavaServer Pages) 같은 동적 웹 컨텐츠를 처리하는 역할을 한다. 아파치 톰캣, Jetty, JBoss 등이 이러한 서블릿 컨테이너의 예이다.
서블릿 컨테이너의 주요 기능 중 하나는 HTTP 세션 관리이다. 사용자가 웹 애플리케이션에 처음 요청을 보낼 때, 서블릿 컨테이너는 해당 사용자를 위한 세션을 생성하고 고유한 세션 ID를 할당한다. 이 세션 ID는 보통 쿠키를 통해 사용자의 브라우저에 저장되며, 이후의 요청에서 이 쿠키를 사용하여 사용자를 식별한다.
- 세션 생성: 사용자가 처음으로 웹 애플리케이션에 접근할 때 새로운 세션을 생성한다.
- 세션 ID 할당: 각 세션에는 고유한 세션 ID가 할당되며, 이는 사용자 식별에 사용된다.
- 세션 데이터 저장: 사용자별 세션 데이터(로그인 상태, 사용자 선택, 장바구니 항목 등)를 서버 메모리나 다른 저장소에 저장한다.
- 세션 만료 및 삭제: 설정된 세션 유효 시간이 지나거나 사용자가 로그아웃할 때 세션을 만료시키고 관련 데이터를 삭제한다.
서블릿 컨테이너는 이러한 세션 정보를 관리하면서 동시에 애플리케이션의 보안과 효율성을 유지한다. 개발자는 서블릿 API를 통해 이러한 세션 관리 기능을 사용할 수 있으며, HttpServletRequest 객체의 메소드를 사용하여 세션에 접근하고, 세션 데이터를 저장하거나 조회할 수 있다.
[ ▷ @SessionAttributes ]
@SessionAttributes 어노테이션은 Spring MVC에서 사용되며, 특정 모델 속성을 HTTP 세션에 자동으로 추가할 수 있도록 지정한다. 이렇게 하면 사용자의 세션 간에 해당 속성을 유지할 수 있으며, 사용자가 여러 요청을 하더라도 일관된 상태를 유지할 수 있게 돕는다.
▼ 사용자의 쇼핑 카트 정보를 세션에 저장하는 예제
@Controller
@SessionAttributes("cart")
public class ShoppingCartController {
@ModelAttribute("cart")
public Cart initializeCart() {
return new Cart(); // 새로운 쇼핑 카트 생성
}
@RequestMapping("/addProduct")
public String addProduct(@ModelAttribute("cart") Cart cart, ...) {
// 상품을 카트에 추가하는 로직
return "redirect:/cart";
}
}
이 예제에서는 cart 모델 속성이 세션에 자동으로 저장된다. 사용자가 요청을 다시 할 때마다 동일한 카트 인스턴스가 사용되므로, 사용자의 상태가 여러 요청간에 유지된다.
- @SessionAttributes는 HTTP 세션에 저장될 모델 속성의 이름만 지정하므로, 해당 이름과 일치하는 속성이 모델에 반드시 있어야 한다.
- 세션에 저장되는 속성은 클라이언트 요청 간에 유지되므로, 사용하지 않게 되면 명시적으로 제거해야 할 수도 있습니다. 그렇지 않으면, 메모리 누수가 발생할 수 있다.
- 이 어노테이션은 상태를 유지해야 하는 웹 어플리케이션의 특정 부분에 유용하게 사용될 수 있지만, 과도한 사용은 애플리케이션의 복잡성을 높일 수 있으므로 주의가 필요하다.
[ ▷ @ModelAttribute ]
@ModelAttribute 어노테이션은 Spring MVC에서 사용되며, 컨트롤러 메서드의 파라미터나 메서드 정의에 사용될 수 있다.
△ 메서드 파라미터에 사용
컨트롤러 메서드의 파라미터에 @ModelAttribute를 사용하면, 해당 파라미가 HTTP 요청의 속성에서 자동으로 바인딩되어야 함을 나타낸다. 이를 사용하여 HTML 폼에서 전송된 데이터를 도메인 객체로 자동으로 바인딩할 수 있다.
▼ 사용자가 폼을 통해 상품 정보를 제출하는 예제
@PostMapping("/addProduct")
public String addProduct(@ModelAttribute Product product) {
// 제품 정보 처리
return "redirect:/products";
}
여기서 Product 객체는 HTTP 요청의 파라미터와 자동으로 매핑되며, 해당 필드에 대한 값들이 자동으로 채워진다.
△ 컨트롤러 메서드로 사용
@ModelAttribute가 적용된 메서드는 해당 컨트롤러의 모든 요청이 처리되기 전에 호출된다. 이러한 메서드의 반환 값은 자동으로 모델에 추가되며, 뷰에서 접근 가능하게 된다.
▼ 모든 뷰에서 사용할 수 있는 공통 속성이 필요한 경우
@ModelAttribute("username")
public String getUsername() {
return "JohnDoe"; // 이 값은 모든 뷰에서 "username" 속성으로 접근 가능
}
△ 세션 속성 바인딩
@SessionAttributes와 함께 사용하면, @ModelAttribute는 세션에 저장된 속성에 접근하는데 사용될 수 있다. 이 경우, @ModelAttribute로 지정된 파라미터는 세션에서 해당 이름의 속성을 찾게 되며, 해당 속성이 없는 경우 모델에서 찾게된다.
@ModelAttribute는 Spring MVC에서 매우 유용하며, 요청 처리의 유연성을 크게 높일 수 있다. 요청 파라미터의 자동 바인딩, 공통 모델 속성의 정의, 세션 속성과의 연동 등 다양한 측면에서 활용될 수 있다.
△ Expires/Max-Age
쿠키의 Expires와 Max-Age 속성은 쿠키의 수명을 결정한다. Expires는 쿠키가 만료되는 정확한 날짜와 시간을 지정한다. 예를 들어, Expires=Wed, 09 Jun 2021 10:18:14 GMT라면 해당 시간에 쿠키는 만료된다. Max-Age는 쿠키가 생성된 후 얼마나 오래 지속될지를 초 단위로 지정한다. 예를 들어, Max-Age=3600은 쿠키가 생성된 시점부터 3600초 (1시간) 후에 만료됨을 의미한다.
쿠키에 Expires나 Max-Age 속성이 Session 이라는 값으로 설정된다면, 이는 표준적인 값이 아니다. Session이라는 값은 일반적으로 세션 쿠키를 의미하는데, 이는 브라우저가 닫힐 때까지만 유지되는 쿠키를 말한다. 세션 쿠키는 Expires나 Max-Age 속성을 설정하지 않음으로써 생성된다. 따라서 쿠키의 Expires나 Max-Age 속성에 Session이라고 명시적으로 서정하는 것은 기술적으루 부적합하다.
△ Secure
Secure 속성은 쿠키가 보안 연결, 즉 HTTPS를 통해서만 전송되어야 함을 지시하는 프랠그이다. 쿠키에 'Secure' 속성이 설정되면, 해당 쿠키는 오직 암호화된 연결을 통해서만 웹 서버로 전송된다. 이는 쿠키 정보가 중간자 공격에 의해 도청되는 것을 방지하여 쿠키 데이터의 안전을 보장하는데 도움이 된다.
웹 서버에 아래와 같은 쿠키가 설정되어 있다고 가정한다.
Set-Cookie: ID=12345; Secure
이 경우, ID 쿠키는 오직 HTTPS 연결을 통해서만 서버로 전송된다. 만약 웹사이트가 HTTP 연결을 통해 접속된다면, 이 쿠키는 요청에 포함되지 않는다. 따라서 'secure' 속성은 주로 로그인 정보나 개인식별 정보와 같은 민감한 데이터를 포함한 쿠키에 사용되며, 데이터 안전한 전송을 보장하는데 중요한 역할을 한다.
△ HttpOnly
HttpOnly 속성은 쿠키를 HTTP(S) 요청을 통해서만 서버에 전송되도록 제한하고, JavaScript 같은 클라이언트 사이드 스크립트를 통한 접근을 금지하는 플래그이다. 이 속성이 설정된 쿠키는 웹 페이지의 JavaScript를 통해 document.cookie를 사용하여 접근할 수 없다.
HttpOnly 속성의 주요 목적은 크로스 사이트 스크립팅 (XSS) 공격으로부터 사용자의 쿠키를 보호하는 것이다. XSS 공격은 공격자가 웹 페이지에 악의적인 스크립트를 주입하여 사용자의 세션 쿠키를 탈취하려고 시도할 수 있는 보안 취약점이다. HttpOnly 속성이 설정된 쿠키는 이러한 스크립트를 통해 접근될 수 없으므로, 사용자의 세션 정보와 같은 민간함 데이터를 보호하는데 효과적이다.
웹 서버에 아래와 같은 쿠키가 설정되어 있다고 가정한다.
Set-Cookie: SessionID=abcdef; HttpOnly
이 경우, SessionID 쿠키는 서버로의 HTTP(S) 요청에는 포함되지만, 웹 페이지의 JavaScript 코드를 통해서만 접근할 수 없다. 이러한 방식으로 HttpOnly 속성은 웹 애플리케이션의 보안을 강화하는데 기여한다.
△ SameSite
SameSite 쿠키 속성은 쿠키가 크로스 사이트 요청과 함께 전송되는 방식을 제어하기 위해 사용된다. 이 속성은 쿠키가 같은 사이트의 요청에만 전송되도록 하거나, 어떤 경우에 크로스 사이트 요청과 함께 쿠키를 전송할지 결정하는데 도움을 준다. SameSite 속성은 크로스 사이트 요청 위조 (CSRF) 공격을 방지하는데 효과적인 방법 중 하나이다.
SameSite 속성은 주로 아래의 세 가지 값 중 하나를 사용한다.
- Strict: 이 설정은 쿠키가 오직 같은 사이트에서 발생한 요청과 함께만 전송되어야 함을 의미한다. 예를 들어, 사용자가 A 사이트에 로그인되어 있고, B 사이트에서 A 사이트로의 링크를 클릭하는 경우, SameSite=Strict로 설정된 쿠키는 요청과 함께 전송되지 않는다. 이 설정은 사용자 경험을 제한할 수 있지만, 보안성은 가장 높다.
- Lax: 이 설정은 일부 크로스 사이트 요청에서 쿠키를 전송할 수 있도록 한다. 예를 들어, 사용자가 직접적으로 링크를 클릭하여 다른 사이트로 이도아는 경우에는 쿠키가 전송될 수 있다. 그러나 크로스 사이트의 이미지 로드나 폼 제출과 같은 경우에는 쿠키가 전송되지 않는다. SameSite=Lax는 대부분의 사용 사례에 적합한 균형 잡인 설정이다.
- None: SameSite=None 설정은 쿠키가 모든 크로스 사이트 요청과 함께 전송될 수 있음을 의미한다. 이 설정을 사용할 때는 반드시 Secure 속성도 함께 설정해야 한다. (SameSite=None; Secure), 이는 쿠키가 오직 안전한 HTTPS 연결을 통해서만 전송되어야 함을 보장한다. 이 설정은 필요한 경우에만 사용해야 하며, 쿠키가 크로스 사이트 요청과 함께 전송되어야 하는 특정 상황에서 유용하다.
SameSite 속성을 적절히 설정함으로써 웹 애플리케이션은 사용자의 데이터 보호를 강화하고, CSRF와 같은 웹 보안 취약점에 대한 보호를 높일 수 있다.
※ 하지만 이 문서는 우리가 사용하는 웹 애플리케이션은 TLS를 지원하지 않으므로 아파치 톰캣 서버의 HTTP 프로토콜을 1.1을 사용하기 때문에 SameSite에 어떠한 값도 들어가지 않는다.
※ / 루트 패스가 정적 리소스 (HTML, CSS, JS)를 제공하는 경우, 이러한 정적 리소스 요청은 세션을 생성하거나 사용하지 않는다. 스프링 부트에서 정적 리소스를 처리하는 요청은 기본적으로 세션과 무관하게 처리된다.
'Spring Boot > Annotations' 카테고리의 다른 글
@RequiredArgsConstructor (0) | 2024.10.24 |
---|---|
Lombok (1) | 2024.10.16 |