-
[๊ฐ์] ์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํต์ฌ ๊ธฐ์ 5SPRING/INFLEARN 2023. 1. 30. 11:46
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2
์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํ์ฉ ๊ธฐ์ - ์ธํ๋ฐ | ๊ฐ์
์น ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ํ์ํ ๋ชจ๋ ์น ๊ธฐ์ ์ ๊ธฐ์ด๋ถํฐ ์ดํดํ๊ณ , ์์ฑํ ์ ์์ต๋๋ค. MVC 2ํธ์์๋ MVC 1ํธ์ ํต์ฌ ์๋ฆฌ์ ๊ตฌ์กฐ ์์ ์ค๋ฌด ์น ๊ฐ๋ฐ์ ํ์ํ ๋ชจ๋ ํ์ฉ ๊ธฐ์ ๋ค์ ํ์ตํ ์ ์
www.inflearn.com
6. ๋ก๊ทธ์ธ ์ฒ๋ฆฌ1 - ์ฟ ํค, ์ธ์
- ๋ก๊ทธ์ธ ์๊ตฌ ์ฌํญ
- ํ ํ๋ฉด - ๋ก๊ทธ์ธ ์
- ํ์ ๊ฐ์
- ๋ก๊ทธ์ธ
- ํ ํ๋ฉด - ๋ก๊ทธ์ธ ํ
- ๋ณธ์ธ ์ด๋ฆ(๋๊ตฌ๋ ํ์ํฉ๋๋ค.)
- ์ํ ๊ด๋ฆฌ
- ๋ก๊ทธ ์์
- ๋ณด์ ์๊ตฌ์ฌํญ
- ๋ก๊ทธ์ธ ์ฌ์ฉ์๋ง ์ํ์ ์ ๊ทผํ๊ณ , ๊ด๋ฆฌํ ์ ์์
- ๋ก๊ทธ์ธ ํ์ง ์์ ์ฌ์ฉ์๊ฐ ์ํ ๊ด๋ฆฌ์ ์ ๊ทผํ๋ฉด ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ์ด๋
- ํ์ ๊ฐ์ , ์ํ ๊ด๋ฆฌ
- ํ๋ก์ ํธ ์์ฑ
- ์์กด๊ด๊ณ๋ ๋จ๋ฐฉํฅ์ผ๋ก ํ๋ฌ์ผ ํ๋ค
๋๋ฉ์ธ์ด ๊ฐ์ฅ ์ค์ํ๋ฏ๋ก ์น์ด ๋๋ฉ์ธ์ ์ฐธ์กฐํ๋๋ก ํด์ผํ๋ค.
์น์ ๋ค์ด๋ด์ ์ฝ๋๋ฅผ ๋ค ๊ฐ์ ์์ด๋ ๋๋ฉ์ธ์๋ ์๋ฌด ๋ฌธ์ ๊ฐ ์๊ธฐ์ง ์๋๋ก
- ํ์ ๊ฐ์
- ๋ก๊ทธ์ธ ํผ html ์ถ๊ฐ
- ๋ก๊ทธ์ธ ๋ ํฌ ์ถ๊ฐ(List๋ฅผ ์ด์ฉํด ์์ ๋ ํฌ, ํ ์คํธ์ฉ ๋ฐ์ดํฐ ๋ฃ๋ ์ฝ๋๋ ํจ๊ป)
- ๋ก๊ทธ์ธ ์ปจํธ๋กค๋ฌ
@Controller @RequiredArgsConstructor @RequestMapping("/members") public class MemberController { private final MemberRepository memberRepository; @GetMapping("/add") public String addForm(@ModelAttribute("member") Member member) { return "members/addMemberForm"; } @PostMapping("/add") public String save(@Valid @ModelAttribute Member member, BindingResult result) { if (result.hasErrors()) { return "members/addMemberForm"; } memberRepository.save(member); return "redirect:/"; }
- ๋ก๊ทธ์ธ ๊ธฐ๋ฅ
- LoginService ๋๋ฉ์ธ์ ์ถ๊ฐ, ๋น์ฆ๋์ค ๋ก์ง , ์์ด๋ ๋น๋ฐ๋ฒํธ ๋ง๋์ง ํ์ธ
@Service @RequiredArgsConstructor public class LoginService { private final MemberRepository memberRepository; public Member login(String loginId, String password) { return memberRepository.findByLoginId(loginId) .filter(m -> m.getPassword().equals(password)) .orElse(null); } }
- ๋ก๊ทธ์ธ ํผ html ์ถ๊ฐ
- ๋ก๊ทธ์ธ ํผ dto ์ถ๊ฐ
- ๋ก๊ทธ์ธ Controller
@Slf4j @Controller @RequiredArgsConstructor public class LoginController { private final LoginService loginService; @GetMapping("/login") public String loginForm(@ModelAttribute("loginForm") LoginForm form) { return "login/loginForm"; } @PostMapping("/login") public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); log.info("login? {}", loginMember); if (loginMember == null) { bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ง์ง ์์ต๋๋ค."); return "login/loginForm"; } //๋ก๊ทธ์ธ ์ฑ๊ณต ์ฒ๋ฆฌ TODO return "redirect:/"; }
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ - ์ฟ ํค ์ฌ์ฉ
๋ก๊ทธ์ธ -> ๋ก๊ทธ์ธ ์ฑ๊ณต
<- ์ฟ ํค ๋ง๋ค์ด์ ๋ณด๋ด์ค
๋ชจ๋ ์์ฒญ์ ์ฟ ํค ์๋ ํฌํจ
์ฟ ํค์๋ ์์, ์ธ์ ์ฟ ํค๊ฐ ์๋๋ฐ ๋ก๊ทธ์ธ์ ๋ธ๋ผ์ฐ์ ์ข ๋ฃ ์๊น์ง๋ง ์ ์ง๋๋ฉด ๋๋ฏ๋ก ์ธ์ ๋ฐฉ์์ ์ ํํ๋ค.
@PostMapping("/login") public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult , HttpServletResponse response) { if (bindingResult.hasErrors()) { return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); log.info("login? {}", loginMember); if (loginMember == null) { bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ง์ง ์์ต๋๋ค."); return "login/loginForm"; } //๋ก๊ทธ์ธ ์ฑ๊ณต ์ฒ๋ฆฌ Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId())); response.addCookie(idCookie); return "redirect:/"; } @PostMapping("/logout") public String logout(HttpServletResponse response) { expireCookie(response, "memberId"); return "redirect:/"; } private void expireCookie(HttpServletResponse response, String cookieName) { Cookie cookie = new Cookie(cookieName, null); cookie.setMaxAge(0); response.addCookie(cookie); }
ํ ์คํธ ๊ฒฐ๊ณผ
home์์๋ ๋ก๊ทธ์ธ์ด ์ ์ง๋ ์ ์๋๋ก ๋ง๋ค๊ธฐ/ ๋ก๊ทธ์์ ๋ง๋ค๊ธฐ
@GetMapping("/") public String homeLogin(@CookieValue(name = "memberId", required = false) Long memberId, Model model) { if (memberId == null) { return "home"; } //๋ก๊ทธ์ธ Member loginMember = memberRepository.findById(memberId); if (loginMember == null) { return "home"; } model.addAttribute("member", loginMember); return "loginHome"; }
๋ก๊ทธ์ธ ํ์๋ ํ ํ๋ฉด์ด ์ด๋ ๊ฒ ๋ฐ๋๋ฉฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ๊ป๋ค๊ฐ ์ผ๋ฉด ์ฌ๋ผ์ง๋ค.
- ์ฟ ํค์ ๋ณด์ ๋ฌธ์
์ฟ ํค๋ ์ฌ์ค ํด๋ผ์ด์ธํธ์์ ์์๋ก ๋ณด๋ผ ์ ์๋ค -> ๋ค๋ฅธ ์ฌ์ฉ์๋ค์ ๊ฐ์ธ์ ๋ณด๊ฐ ๋ค ํธ๋ฆฐ๋ค
ํด์ปค๊ฐ ์ฟ ํค๋ฅผ ํ์ณ์ ์ ์ฉํ ์๋ ์๋ค
-> ๋์
์ฟ ํค์ ์๊ฐ์ ํ์ ์ค๋ค
์์ธก ๋ถ๊ฐ๋ฅํ ์์์ ํ ํฐ์ ์ค๋ค
ํดํน์ด ์์ฌ๋๋ ๊ฒฝ์ฐ ์ฟ ํค๋ฅผ ์ง์ ์ ๊ฑฐํ๋ค
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ - ์ธ์ ๋์ ๋ฐฉ์
์ฟ ํค์ ์ค์ํ ์ ๋ณด๋ฅผ ๋ณด๊ดํ๊ณ ์ฐ๊ฒฐ ์ ์งํ๋ ๋ฐฉ๋ฒ : ์ธ์
์ฟ ํค ๊ฐ์ ๋ณ์กฐ - ์์ ๋ถ๊ฐ๋ฅํ ๋ณต์กํ ์ธ์ id
ํธ๋ ค๋ ๋ณต์กํ๋ฏ๋ก ์๊ธฐ ์ด๋ ต๋ค
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ - ์ธ์ ์ง์ ๋ง๋ค๊ธฐ
@Component public class SessionManager { public static final String SESSION_COOKIE_NAME = "mySessionId"; private Map<String, Object> sessionStore = new ConcurrentHashMap<>(); /** * ์ธ์ ์์ฑ */ public void createSession(Object value, HttpServletResponse response) { //์ธ์ id๋ฅผ ์์ฑํ๊ณ , ๊ฐ์ ์ธ์ ์ ์ ์ฅ String sessionId = UUID.randomUUID().toString(); sessionStore.put(sessionId, value); //์ฟ ํค ์์ฑ Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId); response.addCookie(mySessionCookie); } /** * ์ธ์ ์กฐํ */ public Object getSession(HttpServletRequest request) { Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME); if (sessionCookie == null) { return null; } return sessionStore.get(sessionCookie.getValue()); } /** * ์ธ์ ๋ง๋ฃ */ public void expire(HttpServletRequest request) { Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME); if (sessionCookie != null) { sessionStore.remove(sessionCookie.getValue()); } } private Cookie findCookie(HttpServletRequest request, String cookieName) { if (request.getCookies() == null) { return null; } return Arrays.stream(request.getCookies()) .filter(cookie -> cookie.getName().equals(cookieName)) .findAny() .orElse(null); }
class SessionManagerTest { SessionManager sessionManager = new SessionManager(); @Test void sessionTest() { //์ธ์ ์์ฑ //ํ ์คํธ์ฉ ๊ฐ์ง Mock ์ ๊ณต MockHttpServletResponse response = new MockHttpServletResponse(); Member member = new Member(); sessionManager.createSession(member, response); //์์ฒญ์ ์๋ต ์ฟ ํค ์ ์ฅ MockHttpServletRequest request = new MockHttpServletRequest(); request.setCookies(response.getCookies()); //์ธ์ ์กฐํ Object result = sessionManager.getSession(request); assertThat(result).isEqualTo(member); //์ธ์ ๋ง๋ฃ sessionManager.expire(request); Object expired = sessionManager.getSession(request); assertThat(expired).isNull(); }
Key : UUID
Value : member id๋ก
UUID๋ฅผ ํตํด member id์ ์ ๊ทผ
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ - ์ง์ ๋ง๋ ์ธ์ ์ ์ฉ
@PostMapping("/login") public String loginV2(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) { if (bindingResult.hasErrors()) { return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); log.info("login? {}", loginMember); if (loginMember == null) { bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ง์ง ์์ต๋๋ค."); return "login/loginForm"; } //๋ก๊ทธ์ธ ์ฑ๊ณต ์ฒ๋ฆฌ sessionManager.createSession(loginMember, response); return "redirect:/"; }
@PostMapping("/logout") public String logoutV2(HttpServletRequest request) { sessionManager.expire(request); return "redirect:/"; }
@GetMapping("/") public String homeLoginV2(HttpServletRequest request, Model model) { Member member = (Member)sessionManager.getSession(request); if (member == null) { return "home"; } //๋ก๊ทธ์ธ model.addAttribute("member", member); return "loginHome"; }
sessionManager๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก ๋ชจ๋ ์์ ํ๋ค.
์ด๋ ๊ฒ ์ผ์ผํ ๊ฐ๋ฐํด์ผ ๋๋ ์ธ์ ์ ์๋ธ๋ฆฟ์์ ์ง์ํด์ค๋ค.
์ฟ ํค๊ฐ ์ ์ฅ๋ ๋ชจ์ต - ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ - ์๋ธ๋ฆฟ HTTP ์ธ์ 1
๊ธฐ๋ณธ์ผ๋ก ๋ด์ฅ๋ ๊ธฐ๋ฅ ์ฌ์ฉ
@PostMapping("/login") public String loginV3(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletRequest request) { if (bindingResult.hasErrors()) { return "login/loginForm"; } Member loginMember = loginService.login(form.getLoginId(), form.getPassword()); log.info("login? {}", loginMember); if (loginMember == null) { bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ง์ง ์์ต๋๋ค."); return "login/loginForm"; } //๋ก๊ทธ์ธ ์ฑ๊ณต ์ฒ๋ฆฌ //์ธ์ ์ด ์์ผ๋ฉด ์๋ ์ธ์ ๋ฐํ, ์์ผ๋ฉด ์ ๊ท ์ธ์ ์์ฑ HttpSession session = request.getSession(); //์ธ์ ์ ๋ก๊ทธ์ธ ํ์ ์ ๋ณด ๋ณด๊ด session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember); return "redirect:/"; }
request.getSession(), (true) -> ์ธ์ ์ด ์์ผ๋ฉด ์ธ์ ๋ฐํ, ์์ผ๋ฉด ์์ฑ
(false) -> ์์ผ๋ฉด ๋๊ฐ์ด, ์์ผ๋ฉด null
@PostMapping("/logout") public String logoutV3(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); } return "redirect:/"; }
@GetMapping("/") public String homeLoginV3(HttpServletRequest request, Model model) { HttpSession session = request.getSession(false); if(session == null) return "home"; Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER); if (loginMember == null) { return "home"; } //๋ก๊ทธ์ธ model.addAttribute("member", loginMember); return "loginHome"; }
JSESSIONID๋ผ๋ ์ด๋ฆ์ผ๋ก ์ฟ ํค๊ฐ ์ ์ ํ๊ฒ ์์ฑ๋๋ค
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ - ์๋ธ๋ฆฟ HTTP ์ธ์ 2
- @SessionAttribute
@GetMapping("/") public String homeLoginV3Spring( @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) { if (loginMember == null) { return "home"; } //๋ก๊ทธ์ธ model.addAttribute("member", loginMember); return "loginHome"; }
@SessionAttribute๋ก ์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ์ค์ผ ์ ์๋ค.
์ธ์ ์ ์ฐพ์์จ๋ค(์์ฑํ์ง๋ ์๋๋ค.)
- TrakingModes
์ฟ ํค๋ฅผ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ, URL์ ์ด์ฉํด์ ์ ์ง์ํจ๋ค. ์ฒ์์ ํ๋จ์ด ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ
์ง์ ๋ถํ๋ค.
์ด๋ฅผ ๋๋ ๋ฐฉ๋ฒ์
server.servlet.session.tracking-modes=cookie
๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋ ์ ์๋ค.
- ์ธ์ ์ ๋ณด์ ํ์์์ ์ค์
@Slf4j @RestController public class SessionInfoController { @GetMapping("/session-info") public String sessionInfo(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return "์ธ์ ์ด ์์ต๋๋ค."; } //์ธ์ ๋ฐ์ดํฐ ์ถ๋ ฅ session.getAttributeNames().asIterator() .forEachRemaining(name -> log.info("session name={}, value={}", name, session.getAttribute(name))); log.info("sessionId={}", session.getId()); log.info("maxInactiveInterval={}", session.getMaxInactiveInterval()); log.info("creationTime={}", new Date(session.getCreationTime())); log.info("lastAccessedTime={}", new Date(session.getLastAccessedTime())); log.info("isNew={}", session.isNew()); return "์ธ์ ์ถ๋ ฅ"; } }
์ธ์ ์ ์ ๋ณด๋ค์ ๋ณธ๋ค.
์ธ์ ์ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์์์ ์ง์ ํธ์ถํ ๋๋ง ์ญ์ ๋๋ค.
ํ์ง๋ง ๋๋ถ๋ถ์ ์ฌ์ฉ์๋ค์ ์ด๋ฅผ ์ ํํ์ง ์๊ณ , HTTP๋ ๋น์ฐ๊ฒฐ์ฑ์ด๋ฏ๋ก ์๋ฒ ์ ์ฅ์์๋ ์ฌ์ฉ์๊ฐ ์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ข ๋ฃํ ๊ฒ์ธ์ง ์๋์ง ์ธ์ํ ์ ์๋ค.
์๋ฒ์์ ์ธ์ ๋ฐ์ดํฐ๋ฅผ ์ธ์ ์ญ์ ํด์ผ๋ ๊น? - ์ผ๋จ ๋ฌดํ์ ์ ์๋จ, ๋ณด์์/ ๋ฉ๋ชจ๋ฆฌ์
๋ฉ๋ชจ๋ฆฌ์ ๋ฐ์ดํฐ๊ฐ ์ฐ๋๋ ์์ด๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋๋ค.
-> ์ธ์ ์์ ์ต์ํ์ ๋ฐ์ดํฐ๋ง ๋ณด๊ดํด์ผํ๋ค.
-> ์ฌ์ฉ์๊ฐ ์๋ฒ์ ์ต๊ทผ์ ์์ฒญํ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก 30๋ถ์ ์ ์งํด์ฃผ๋ฉด ์ด๋จ๊น?
- ๊ธ๋ก๋ฒ์ค์
server.servlet.session.timeout=60
- ์ธ์ ๋จ์๋ก ์ค์
session.setMaxInactiveInterval(1800);
- ์ธ์ ํ์์์ ๋ฐ์
์ธ์ ํ์์์ ์๊ฐ์ ํด๋น ์ธ์ ๊ณผ ๊ด๋ จ๋ JSESSIONID๋ฅผ ์ ๋ฌํ๋ HTTP ์์ฒญ์ด ์์ ๋๋ง๋ค ์ด๊ธฐํ์ํจ๋ค.
LastAccessedTime(session.getLastAccessedTime()) ์ดํ๋ก timeout ์๊ฐ์ด ์ง๋๋ฉด WAS๊ฐ ๋ด๋ถ์์ ํด๋น ์ธ์ ์ ์ ๊ฑฐ
7. ๋ก๊ทธ์ธ ์ฒ๋ฆฌ2 - ํํฐ, ์ธํฐ์ ํฐ
- ์๋ธ๋ฆฟ ํํฐ - ์๊ฐ
URL๋ก ๋ง ์ฌ๊ธฐ์ ๊ธฐ ๋ค์ด๊ฐ ์ ์์ผ๋ฉด ๋ก๊ทธ์ธ ๋์ง ์์ ์ฌ์ฉ์๊ฐ ๋ค์ด๊ฐ๋ฉด ์๋๋ ๊ณณ๊น์ง ๋ค์ด๊ฐ์ง๋ค = ๋ฌธ์
์ด๋ฅผ
ํํฐ : ์๋ธ๋ฆฟ์ด
์ธํฐ์ ํฐ : ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ธฐ๋ฅ
๋ก๊ทธ์ธ ์ฌ๋ถ ์ฒดํฌ๋ฅผ ๋งค api ํธ์ถ ๋๋ง๋ค ํ๋๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋ณ๋ก๋ค.
์ด๋ฐ ๊ณตํต๊ด์ฌ์ฌ๋ ์คํ๋ง AOP๋ก๋ ํด๊ฒฐํ ์ ์์ง๋ง, ์น๊ณผ ๊ด๋ จํ ๊ณตํต ๊ด์ฌ์ฌ๋ ์๋ธ๋ฆฟ ํํฐ, ์คํ๋ง ์ธํฐ์ ํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.(HttpServletRequest๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ)
์๋ธ๋ฆฟ ํํฐ : ์๋ธ๋ฆฟ์ด ์ง์ํ๋ ์๋ฌธ์ฅ
URL ํจํด๋ณ๋ก ์ ์ฉํ ์ ์์(/* = ๋ชจ๋ ์์ฒญ)
ํํฐ ํ๋ฆ
HTTP ์์ฒญ -> WAS -> ํํฐ -> ์๋ธ๋ฆฟ -> ์ปจํธ๋กค๋ฌ
ํํฐ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํ, ๋ฑ๋กํ๋ฉด
์๋ธ๋ฆฟ ์ปจํ ์ด๋๊ฐ ์ฑ๊ธํค ๊ฐ์ฒด๋ก ์์ฑํ๊ณ ๊ด๋ฆฌํจ.
init(): ํํฐ ์ด๊ธฐํ ๋ฉ์๋, ์๋ธ๋ฆฟ ์ปจํ ์ด๋๊ฐ ์์ฑ๋ ๋ ํธ์ถ๋๋ค.
doFilter(): ๊ณ ๊ฐ์ ์์ฒญ์ด ์ฌ ๋ ๋ง๋ค ํด๋น ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค. ํํฐ์ ๋ก์ง์ ๊ตฌํํ๋ฉด ๋๋ค.
destroy(): ํํฐ ์ข ๋ฃ ๋ฉ์๋, ์๋ธ๋ฆฟ ์ปจํ ์ด๋๊ฐ ์ข ๋ฃ๋ ๋ ํธ์ถ๋๋ค.- ์๋ธ๋ฆฟ ํํฐ - ์์ฒญ ๋ก๊ทธ
@Slf4j public class LogFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("log filter init"); } //HTTP ์์ฒญ์ด ์ฌ ๋๋ง๋ค ํธ์ถ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String requestURI = httpRequest.getRequestURI(); String uuid = UUID.randomUUID().toString(); try { log.info("REQUEST [{}][{}]", uuid, requestURI); chain.doFilter(request, response); } catch (Exception e) { throw e; } finally { log.info("RESPONSE [{}][{}]", uuid, requestURI); } } @Override public void destroy() { log.info("log filter destroy"); } }
ServletRequest request ๋ HTTP ์์ฒญ์ด ์๋ ๊ฒฝ์ฐ๊น์ง ๊ณ ๋ คํด์ ๋ง๋ ์ธํฐํ์ด์ค์ด๋ค. HTTP๋ฅผ
์ฌ์ฉํ๋ฉด HttpServletRequest httpRequest = (HttpServletRequest) request; ์ ๊ฐ์ด ๋ค์ด ์ผ์คํ ํ๋ฉด ๋๋ค.String uuid = UUID.randomUUID().toString();
HTTP ์์ฒญ์ ๊ตฌ๋ถํ๊ธฐ ์ํด ์์ฒญ๋น ์์์ uuid ๋ฅผ ์์ฑํด๋๋ค.chain.doFilter(request, response);
๋ค์ ํํฐ๊ฐ ์์ผ๋ฉด ํํฐ๋ฅผ ํธ์ถํ๊ณ , ํํฐ๊ฐ ์์ผ๋ฉด ์๋ธ๋ฆฟ์ ํธ์ถํ๋ค.
๋ง์ฝ ์ด ๋ก์ง์ ํธ์ถํ์ง ์์ผ๋ฉด ๋ค์ ๋จ๊ณ๋ก ์งํ๋์ง ์๋๋ค.@Configuration public class WebConfig { @Bean public FilterRegistrationBean logFilter() { FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new LogFilter()); //ํํฐ๋ ์ฒด์ธ์ผ๋ก ๋์ํ๋ค. ๋ฐ๋ผ์ ์์๊ฐ ํ์ํ๋ค. ๋ฎ์ ์๋ก ๋จผ์ ๋์ํ๋ค. filterRegistrationBean.setOrder(1); filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; } }
- ์๋ธ๋ฆฟ ํํฐ - ์ธ์ฆ ์ฒดํฌ
@Slf4j public class LoginCheckFilter implements Filter { //ํ์ด์ค url ๋ฆฌ์คํธ private static final String[] whitelist = {"/", "/members/add", "/login", "/logout","/css/*"}; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String requestURI = httpRequest.getRequestURI(); HttpServletResponse httpResponse = (HttpServletResponse) response; try { log.info("์ธ์ฆ ์ฒดํฌ ํํฐ ์์ {}", requestURI); if (isLoginCheckPath(requestURI)) { log.info("์ธ์ฆ ์ฒดํฌ ๋ก์ง ์คํ {}", requestURI); HttpSession session = httpRequest.getSession(false); if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) { log.info("๋ฏธ์ธ์ฆ ์ฌ์ฉ์ ์์ฒญ {}", requestURI); //๋ก๊ทธ์ธ์ผ๋ก redirect httpResponse.sendRedirect("/login?redirectURL=" + requestURI); return; //์ฌ๊ธฐ๊ฐ ์ค์, ๋ฏธ์ธ์ฆ ์ฌ์ฉ์๋ ๋ค์์ผ๋ก ์งํํ์ง ์๊ณ ๋! } } chain.doFilter(request, response); } catch (Exception e) { throw e; //์์ธ ๋ก๊น ๊ฐ๋ฅ ํ์ง๋ง, ํฐ์บฃ๊น์ง ์์ธ๋ฅผ ๋ณด๋ด์ฃผ์ด์ผ ํจ } finally { log.info("์ธ์ฆ ์ฒดํฌ ํํฐ ์ข ๋ฃ {}", requestURI); } } /** * ํ์ดํธ ๋ฆฌ์คํธ์ ๊ฒฝ์ฐ ์ธ์ฆ ์ฒดํฌX */ private boolean isLoginCheckPath(String requestURI) { return !PatternMatchUtils.simpleMatch(whitelist, requestURI); } }
webconfig์ ๋ฑ๋ก
@Bean public FilterRegistrationBean loginCheckFilter() { FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new LoginCheckFilter()); filterRegistrationBean.setOrder(2); filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; }
-> ์๋ฌด๋ฆฌ ๋ค๋ฅธ ํ์ด์ง๋ฅด ๋ค์ด๊ฐ๋ คํด๋ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฐ์ ๋ค์ด๊ฐ ์ ์๋ค.
@PostMapping("/login") public String loginV4(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, @RequestParam(defaultValue = "/") String redirectURL, HttpServletRequest request) { /*์๋ต*/ return "redirect:" + redirectURL; }
์ด๋ ๊ฒ ํ๋ฉด ์๋ ๊ฐ๋ ค๋ ํ์ด์ง๋ก ๋์๊ฐ ์ ์๋ค.
- ์คํ๋ง ์ธํฐ์ ํฐ - ์๊ฐ
HTTP ์์ฒญ -> WAS -> ํํฐ -> ์๋ธ๋ฆฟ -> ์คํ๋ง ์ธํฐ์ ํฐ -> ์ปจํธ๋กค๋ฌ
์๋ธ๋ฆฟ ํํฐ์ ๋นํด ์ ๋ฐํ ์ค์ ์ด ๊ฐ๋ฅํ๋ค
preHandle
์ปจํธ๋กค๋ฌ ํธ์ถ ์ ์ ํธ์ถ๋๋ค. (๋ ์ ํํ๋ ํธ๋ค๋ฌ ์ด๋ํฐ ํธ์ถ ์ ์ ํธ์ถ๋๋ค.)
์์ธ ๋ฐ์ ์์๋ ์ปจํธ๋กค๋ฌ ํธ์ถ ์ ์ ํธ์ถ๋๋ค.
preHandle ์ ์๋ต๊ฐ์ด true ์ด๋ฉด ๋ค์์ผ๋ก ์งํํ๊ณ , false ์ด๋ฉด ๋๋ ์งํํ์ง ์๋๋ค. false
์ธ ๊ฒฝ์ฐ ๋๋จธ์ง ์ธํฐ์ ํฐ๋ ๋ฌผ๋ก ์ด๊ณ , ํธ๋ค๋ฌ ์ด๋ํฐ๋ ํธ์ถ๋์ง ์๋๋ค. ๊ทธ๋ฆผ์์ 1๋ฒ์์ ๋์ด
๋๋ฒ๋ฆฐ๋ค.
postHandle์ปจํธ๋กค๋ฌ ํธ์ถ ํ์ ํธ์ถ๋๋ค. (๋ ์ ํํ๋ ํธ๋ค๋ฌ ์ด๋ํฐ ํธ์ถ ํ์ ํธ์ถ๋๋ค.)
์ปจํธ๋กค๋ฌ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ํธ์ถ๋์ง ์๋๋ค.
afterCompletion๋ทฐ๊ฐ ๋ ๋๋ง ๋ ์ดํ์ ํธ์ถ๋๋ค.
์์ธ๊ฐ ๋ฐ์ํด๋ ํธ์ถ๋๋ค.
- ์คํ๋ง ์ธํฐ์ ํฐ - ์์ฒญ ๋ก๊ทธ
@Slf4j public class LogInterceptor implements HandlerInterceptor { public static final String LOG_ID = "logId"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); String uuid = UUID.randomUUID().toString(); request.setAttribute(LOG_ID, uuid); //@RequestMapping: HandlerMethod //์ ์ ๋ฆฌ์์ค: ResourceHttpRequestHandler if (handler instanceof HandlerMethod) { //ํธ์ถํ ์ปจํธ๋กค๋ฌ ๋ฉ์๋์๋ชจ๋ ์ ๋ณด๊ฐ ํฌํจ๋์ด ์๋ค. HandlerMethod hm = (HandlerMethod) handler; } log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler); return true; //false ์งํX } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle [{}]", modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String requestURI = request.getRequestURI(); String logId = (String)request.getAttribute(LOG_ID); log.info("RESPONSE [{}][{}]", logId, requestURI); if (ex != null) { log.error("afterCompletion error!!", ex); } } }
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()) .order(1) .addPathPatterns("/**") .excludePathPatterns("/css/**", "/*.ico", "/error"); }
registry.addInterceptor(new LogInterceptor()) : ์ธํฐ์ ํฐ๋ฅผ ๋ฑ๋กํ๋ค.
order(1) : ์ธํฐ์ ํฐ์ ํธ์ถ ์์๋ฅผ ์ง์ ํ๋ค. ๋ฎ์ ์๋ก ๋จผ์ ํธ์ถ๋๋ค.
addPathPatterns("/**") : ์ธํฐ์ ํฐ๋ฅผ ์ ์ฉํ URL ํจํด์ ์ง์ ํ๋ค.
excludePathPatterns("/css/**", "/*.ico", "/error") : ์ธํฐ์ ํฐ์์ ์ ์ธํ ํจํด์ ์ง์ ํ๋ค.HandlerMethod
ํธ๋ค๋ฌ ์ ๋ณด๋ ์ด๋ค ํธ๋ค๋ฌ ๋งคํ์ ์ฌ์ฉํ๋๊ฐ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ค. ์คํ๋ง์ ์ฌ์ฉํ๋ฉด ์ผ๋ฐ์ ์ผ๋ก
@Controller , @RequestMapping ์ ํ์ฉํ ํธ๋ค๋ฌ ๋งคํ์ ์ฌ์ฉํ๋๋ฐ, ์ด ๊ฒฝ์ฐ ํธ๋ค๋ฌ ์ ๋ณด๋ก
HandlerMethod ๊ฐ ๋์ด์จ๋ค.
ResourceHttpRequestHandler
@Controller ๊ฐ ์๋๋ผ /resources/static ์ ๊ฐ์ ์ ์ ๋ฆฌ์์ค๊ฐ ํธ์ถ ๋๋ ๊ฒฝ์ฐ
ResourceHttpRequestHandler ๊ฐ ํธ๋ค๋ฌ ์ ๋ณด๋ก ๋์ด์ค๊ธฐ ๋๋ฌธ์ ํ์ ์ ๋ฐ๋ผ์ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.postHandle, afterCompletion
์ข ๋ฃ ๋ก๊ทธ๋ฅผ postHandle ์ด ์๋๋ผ afterCompletion ์์ ์คํํ ์ด์ ๋, ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ
postHandle ๊ฐ ํธ์ถ๋์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. afterCompletion ์ ์์ธ๊ฐ ๋ฐ์ํด๋ ํธ์ถ ๋๋ ๊ฒ์ ๋ณด์ฅํ๋ค.PathPattern (Spring Framework 6.0.4 API)
Compare this pattern with a supplied pattern: return -1,0,+1 if this pattern is more specific, the same or less specific than the supplied pattern.
docs.spring.io
- ์คํ๋ง ์ธํฐ์ ํฐ - ์ธ์ฆ ์ฒดํฌ
@Slf4j public class LoginCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("์ธ์ฆ ์ฒดํฌ ์ธํฐ์ ํฐ ์คํ {}", requestURI); HttpSession session = request.getSession(false); if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) { log.info("๋ฏธ์ธ์ฆ ์ฌ์ฉ์ ์์ฒญ"); //๋ก๊ทธ์ธ์ผ๋ก redirect response.sendRedirect("/login?redirectURL=" + requestURI); return false; } return true; } }
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()) .order(1) .addPathPatterns("/**") .excludePathPatterns("/css/**", "/*.ico", "/error"); registry.addInterceptor(new LoginCheckInterceptor()) .order(2) .addPathPatterns("/**") //๋ชจ๋ ๊ณณ์ ์ ์ฉ .excludePathPatterns( "/", "/members/add", "/login", "/logout", "/css/**", "/*.ico", "/error" //ํ์ง๋ง ์ด๊ฑด ๋นผ ); }
์ฝ๋๊ฐ ์๋ธ๋ฆฟ์ ๋นํด์ ๋งค์ฐ ๊ฐ๋จํ๋ค
ํน๋ณํ ์ด์ ๊ฐ ์๋ค๋ฉด ์ด๊ฒ์ ์ฌ์ฉํ์
- ArgumentResolver ํ์ฉ
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface Login { }
@Target(ElementType.PARAMETER) : ํ๋ผ๋ฏธํฐ์๋ง ์ฌ์ฉ
@Retention(RetentionPolicy.RUNTIME) : ๋ฆฌํ๋ ์ ๋ฑ์ ํ์ฉํ ์ ์๋๋ก ๋ฐํ์๊น์ง ์ ๋ ธํ ์ด์ ์ ๋ณด๊ฐ ๋จ์์์@GetMapping("/") public String homeLoginV3ArgumentResolver(@Login Member loginMember, Model model) {
@Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new LoginMemberArgumentResolver()); }
@Slf4j public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { log.info("supportsParameter ์คํ"); boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class); boolean hasMemberType = Member.class.isAssignableFrom(parameter.getParameterType()); return hasLoginAnnotation && hasMemberType; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { log.info("resolveArgument ์คํ"); HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); HttpSession session = request.getSession(false); if (session == null) { return null; } return session.getAttribute(SessionConst.LOGIN_MEMBER); } }
๊ฒฐ๊ณผ๋ ๋์ผํ๊ฒ, ์ ๋ ธํ ์ด์ ์ผ๋ก ๊ฐ๋จํ๊ฒ ํด๊ฒฐํ ์ ์์
'SPRING > INFLEARN' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[๊ฐ์] ์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํต์ฌ ๊ธฐ์ 4 (0) 2023.03.20 [๊ฐ์] ์คํ๋ง MVC 1ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํต์ฌ ๊ธฐ์ 1 (0) 2023.03.13 [๊ฐ์] ์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํต์ฌ ๊ธฐ์ 3 (0) 2023.01.13 [๊ฐ์] ์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํต์ฌ ๊ธฐ์ 1 (0) 2022.11.21 [๊ฐ์] ์ค์ ! Querydsl 1 (0) 2022.08.22 - ํ ํ๋ฉด - ๋ก๊ทธ์ธ ์