Spring MVC에서 Controller가 처리한 결과 데이터를 뷰(View)로 전달하기 위해 사용하는 객체
Model은 Map<String, Object> 형태로 데이터를 저장한다. Model, ModelMap, ModelAndView 중 하나를 사용한다. ${속성명} 으로 접근 가능하다.| 메서드 | 설명 |
|---|---|
addAttribute(String key, Object value) | 단일 데이터를 추가할 때 사용합니다. Key를 기준으로 값을 저장합니다. |
addAllAttributes(Map<String, ?> map) | 여러 데이터를 한 번에 추가할 수 있습니다. Map에 있는 모든 엔트리를 모델에 넣습니다. |
mergeAttributes(Map<String, ?> map) | addAllAttributes와 비슷하지만, 기존 키가 이미 존재하면 덮어쓰지 않고 유지합니다. 즉, 병합(Merge)입니다. |
@GetMapping("/example")
public String example(Model model) {
model.addAttribute("title", "Model 설명");
Map<String, Object> map = new HashMap<>();
map.put("author", "관리자");
map.put("views", 123);
model.addAllAttributes(map); // author, views 추가
Map<String, Object> mergeMap = new HashMap<>();
mergeMap.put("title", "다른 제목"); // 기존 "title"이 있으므로 병합 시 무시됨
mergeMap.put("likes", 99);
model.mergeAttributes(mergeMap); // "likes"는 추가되고, "title"은 유지됨
return "modelPage";
}
addAtribute() 메소드로 데이터 저장String으로 반환@GetMapping("/user")
public String gerUser(Model model) {
model.addAttribute("name", "짱구");
return "viewPage"; // 뷰 이름
}
java.util.Map을 상속한 객체 Model보다 유연하고, put() 사용 가능String으로 반환@GetMapping("/user")
public String gerUser(ModelMap modelMap) {
modelMap.put("name", "짱구");
return "viewPage";
}
ModelAndView@GetMapping("/modelandview")
public ModelAndView gerUser() {
ModelAndView mav = new ModelAndView("viewPage");
mav.addObject("name", "짱구");
return mav;
}
| 항목 | Model | ModelMap | ModelAndView |
|---|---|---|---|
| 타입 | 인터페이스 (Model) | 클래스 (ModelMap extends HashMap) | 클래스 (ModelAndView) |
| 뷰 이름 지정 | return "view" | return "view" | new ModelAndView("view") |
| 데이터 추가 방법 | addAttribute("key", value) | put("key", value) | addObject("key", value) |
| 선언 위치 | 메서드 파라미터 | 메서드 파라미터 | 반환 타입으로 사용 |
| 추천 사용 용도 | 단순 데이터 전달 | Model과 유사, 유연한 Map 사용 | 뷰와 데이터를 함께 설정하고 싶을 때 |
| 상황 | 추천 방식 |
|---|---|
| 가장 기본적인 컨트롤러 설계 | Model |
| Map 스타일로 유연하게 쓰고 싶을 때 | ModelMap |
| 데이터 + 뷰 이름을 명시적으로 반환하고 싶을 때 | ModelAndView |
요청 -> Controller -> Modewl에 데이터 담기 -> View 렌더링
컨트롤러는 비즈니스 로직 처리 후 그 결과 데이터를 뷰(View)에 전달해야 하는데, 그 전달 도구가 Model이다.
Spring 기반 애플리케이션의 보안(인증, 인가)을 담당하는 프레임워크
웹 애플리케이션에서 로그인, 권한 관리, CSRF 보호, 세션 관리 같은 보안 기능을 쉽게 구현할 수 있게 도와준다.
| 구분 | 설명 |
|---|---|
| 인증 (Authentication) | 사용자가 누구인지 확인 (ex. 로그인) |
| 인가 (Authorization) | 인증된 사용자가 무엇을 할 수 있는지 결정 (ex. 관리자만 글 삭제 가능) |
| Filter 기반 구조 | SecurityFilterChain으로 요청을 감시하고 처리 |
| 비밀번호 암호화 | BCryptPasswordEncoder 등을 통해 안전하게 암호화 |
| 세션 관리 | 로그인 유지, 세션 고정 보호 등 제공 |
| CSRF 보호 | CSRF 공격을 막기 위한 기본 설정 포함 |
| 기본 로그인 화면 제공 | 설정하지 않으면 기본 로그인 폼 자동 생성 |
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/", "/login", "/css/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(withDefaults()) // 기본 로그인 폼 사용
.logout(withDefaults());
return http.build();
}
}
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 실제로는 DB에서 사용자 조회
return User.withUsername("user")
.password(passwordEncoder().encode("1234"))
.roles("USER")
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
| 어노테이션 | 설명 |
|---|---|
@EnableWebSecurity | Spring Security 설정을 활성화 |
@PreAuthorize("hasRole('ADMIN')") | 메서드 실행 전에 권한 체크 |
@Secured("ROLE_USER") | 특정 역할에만 접근 허용 |
@WithMockUser | 테스트 시 가짜 사용자 설정 |
공격자가 세션 ID를 미리 정해놓고, 사용자가 그 세션을 쓰게 만든 뒤 해당 세션으로 접근하는 공격
-> 공격자는 정상 로그인 없이 인증된 사용자가 되며 세션이 유지되는 동안 피해자의 권한으로 정보 탈취, 조작이 가능하다.
Spring Security는 로그인 시 기존 세션을 무효화하고 새로운 세션을 생성함으로써 세션 고정 공격을 기본적으로 방지한다.
http
.sessionManagement()
.sessionFixation().migrateSession(); // 기본값
오늘은 모델 객체와 Spring Security의 개념에 대해 공부했다.
Model, ModelMap, ModelAndView는 모두 컨트롤러에서 뷰로 데이터를 전달할 때 사용되며, 상황에 따라 적절히 선택하여 사용할 수 있다. 특히 Model이 가장 직관적이고 자주 사용된다는 점에서 잘 익혀두는 것이 중요하다고 느꼈다.
또한 Spring Security에서는 인증과 인가를 어떻게 처리하는지, 기본 로그인 설정 외에도 세션 고정 공격 같은 보안 이슈를 어떻게 예방하는지에 대해서도 알게 되었다. 단순히 로그인만 구현하는 것이 아니라 보안까지 고려한 설계가 필요하다는 것을 느꼈다.