티스토리 뷰
개발자(혹은 우리)가 로그인 기능을 구현할 때는 고려할 부분이 생각보다 많습니다.
예를 들어, 로그인 페이지에서 아이디와 비밀번호를 서버로 전송했을 때, 서버에서 로그인을 처리하는 로직의 파악해야 하고, 이렇게 한 번 로그인 한뒤에는 동일한 클라이언트로부터 요청이 오면 이 로그인 상태가 유지되어야 합니다.
보통 이러한 로그인 상태는 쿠키(Cookie) 혹은 세션(Session)으로 관리를 하며 여기에 해당 키의 유효시간 관리를 통해 일정시간만 유지되도록 할 수 있습니다.
단순히 스프링 프레임워크를 공부 중이라면 아직은 관련없겠지만, 스프링 시큐리티라는 프레임워크로 로그인, 계층화 등 다양한 기능을 제공하지만 결국 이러한 스프링 시큐리티도 쿠키, 세션을 통해 관리하는 것이고 여러 리졸버를 이용합니다.
이번 포스팅에서는 쿠키와 세션을 통해 로그인을 처리하는 과정을 알아보도록 하겠습니다.
그리고 로그인 처리 2번째 포스팅에서는 필터와 인터셉터에 대해 알아보겠습니다.
쿠키(Cookie)
쿠키란 클라이언트를 식별할 때 사용하는 데이터로 클라이언트(브라우저) 로컬에 저장되는 이름과 값의 쌍으로 구성된 작은 정보를 의미합니다.
기본적으로 아스키 문자만 저장이 가능합니다.(한글은 URL 인코딩 필요)
이외에도 도메인 정보, path 정보, 유효기간 등을 지정할 수 있습니다.
서버에서 생성 후 응답과 함께 클라이언트에 전송합니다.
브라우저에 저장되며 유효기간 이후에는 자동으로 삭제됩니다.
쿠키의 동작 방식
- 클라이언트가 페이지를 요청합니다.
- 서버에서 쿠키를 생성합니다.
- HTTP 헤더에 쿠키를 포함시켜 응답합니다.
- 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있습니다.(세션 쿠키)
- 같은 요청을 할 경우 HTTP 헤더에 쿠키를 함께 보냅니다.
- 서버에서 쿠키를 읽어 이전 상태 정보를 변경할 필요가 있을 때 쿠키를 업데이트 하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답합니다.
쿠키의 종류
사용자는 상황에 따라 쿠키의 생명주기를 설정하여 사용할 수 있습니다.
영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지
세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시까지만 유지
서버에서 쿠키 생성하기
아래 예제 코드들은 스프링을 이용하여 서버에서 쿠키를 생성 및 조회하는 코드입니다.
java.servlet.http 에는 Cookie 라는 클래스를 제공해주는데 이 클래스를 이용해 클라이언트에게 응답할 쿠키정보를 생성할 수 있습니다.
[예제 코드 - 서버에서 쿠키 생성]
//기본 예시
Cookie idCookie = new Cookie("id", "testID"); //쿠키 생성
cookie.setMaxAge(6000); //유효기간 설정(초), 100분
response.addCookie(idCookie); //응답에 쿠키 추가
----------------------------------------------------------------
//로그인 로직
@PostMapping("login")
public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
//LoginForm에 검증 오류가 있다면 실행되는 로직
if (bingdingResult.hasErrors()) {
return "login/loginForm";
}
//로그인 검증 로직
//loginService는 사용자 정의 클래스 -> 로그인 기능을 위해 만들어둠
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember==null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
//쿠키에 시간 정보를 주지 않으면 세션 쿠키가 된다. (브라우저 종료시 모두 종료)
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
return "redirect:/";
}
위와 같이 쿠키를 만들때 생성자로 key/value 를 인수로 넘겨주어 생성해야 합니다.
추가로 생성된 쿠키를 response.addCookie()의 파라미터로 넘겨주면 웹 브라우저에서는 Set-Cookie 프로퍼티에 쿠키정보가 담겨져 반환되게 됩니다.
[예제 코드 - 쿠키 조회]
@GetMapping("/")
public String homeLogin(@CookieValue(name = "memberId", required = fasle) Long memberId, Model model) {
if (memberId=null) {
return "home";
}
Member loginMember = memberRespository.findById(memberId);
if (loginMember==null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
@CookieValue 애노테이션을 사용하면 쿠키를 편하게 조회할 수 있도록 해준다.
전송된 쿠키정보 중 "memberId" 인 쿠키값을 찾아 memberId 변수에 할당해줍니다.
required 속성이 false이기 때문에 쿠키정보가 없는 비회원도 접근 가능합니다.
(required 속성값 생략시 오류)
[예제 코드 - 쿠키 없애기(로그아웃)]
로그아웃 기능은 쿠키를 삭제하는게 아니라 종료 날짜를 0으로 설정해서 바로 만료시킴으로써 삭제할 수 있습니다.
@PostMapping("/logout")
public String logout(HttpServletResponse response) {
expiredCookie(response, "memberId");
retrun "redirect:/";
}
private void expiredCookie(HttpServletResponse response, String cookieName) {
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
응답 쿠키의 정보를 보면 Max-Age=0으로 설정되어 해당 쿠키는 즉시 종료됩니다.
쿠키 로그인 처리의 문제점
- 쿠키 값을 임의대로 변경할 수 있습니다.
- 쿠키에 보관된 정보를 타인이 훔쳐갈 수 있습니다.
- 한 번 도용된 쿠키정보는 계속 악용될 수 있습니다.
정리하자면, 중요한 개인정보들이 클라이언트에 저장되어 있기 때문에 위변조 및 도용이 쉽다는 문제가 있습니다.
그런 문제점을 해결하기 위해서는 이런 중요한 정보들을 클라이언트가 아니라 서버에서 관리하도록 하고 그 정보가 외부로 노출되지 않도록 해야합니다.
그래서 클라이언트는 서버가 보관하고 있는 중요 정보에 접근할 수 있는 키만 가지고 있고, 이 키 또한 유효시간을 짧게 둬서 갱신되도록 하면 보안적으로 많이 안전해질 것입니다. 그리고 이렇게 중요한 정보를 보관 및 연결 유지 방법을 세션이라고 합니다.
세션(Session)
세션은 쿠키를 기반으로 하고 있지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 저장되고 관리합니다.
서버에서 클라이언트를 구분하기 위해 세션 ID를 부여하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증상태를 유지합니다. 즉, 세션ID를 통해 서로 관련된 요청을 묶어주는 역할을 합니다. 같은 세션에 포함되어있는 동안 session 객체를 공유할 수 있습니다.
세션의 동작 방식
- 클라이언트가 서버에 첫 번째 요청을 보내면 서버에서 세션 객체를 생성합니다. 클라이언트에 세션 정보를 쿠키를 통해 전달합니다.
- 클라이언트는 세션 ID에 대한 정보를 쿠키를 사용해서 저장하고 가집니다.
- 이후 클라이언트는 서버에 요청할 때, 쿠키의 세션 ID를 같이 서버에 전달해서 요청합니다.
- 서버는 쿠키에 저장된 세션 ID를 전달 받아서 같은 세션 ID의 세션객체에 접근하여 클라이언트 정보를 사용할 수 있다.
- 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답합니다.
이제 로그인이나 페이지 접근시 쿠키에선 저장하고 있는 세션 ID를 같이 전달하면 서버의 세션저장소에서는 해당 세션 ID를 key로 가지고 있는 value 값을 조회해서 로그인 여부와 중요 정보를 확인합니다.
클라이언트와 서버는 결국엔 쿠키로 연결되어 있지만 중요한점은 다음과 같다.
회원과 관련된 정보는 클라이언트에서 가지고 있지 않다.
추정 불가능한 세션 아이디만 쿠키를 통해 주고받기에 보안에서 많이 안전해졌다.
여기에 추가로 세션 ID가 저장된 쿠키의 만료시간을 짧게 유지한다면, 해커가 해당 키를 도용하더라도 금방 갱신되어 사용하지 못하게 되기때문에 보안적으로 좀 더 안전해질 수 있습니다.
세션 관리 기능
세션은 다음과 같이 크게 3가지 기능을 제공해야 한다.
- 세션 생성
- 세션키는 중복이 안되며 추정 불가능한 랜덤 값이어야 한다.
- 세션 키에 매칭될 값(value)이 있어야 한다.
- 이렇게 생성된 세션 키를 응답 쿠키에 저장해 클라이언트에 전달해야 한다.
- 세션 조회
- 클라이언트가 요청한 세션 ID 쿠키 값으로 세션 저장소에 저장된 값을 조회할 수 있어야 한다.
- 세션 만료
- 클라이언트가 요청한 세션 ID 쿠키 값으로 세션 저장소에 보관한 세션 엔트리를 제거해야 한다.
로그인 처리하기 - 세션 직접 만들어보기
위에서 설명한 세션이 제공해야하는 3가지 기능을 직접 구현해보자
혹시나 코드가 이해가 되지않는다면, 이런식이구나하고 넘어가도록 하자.
[예제 코드]
@Component
public class SessionManager {
public static final String SESSION_COOKIE_NAME = "mySessionId";
//HashMap은 동시성 문제가 있기 때문에, ConcurrentHashMap을 사용
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());
}
}
//요청 들어온 쿠키가 없다면 Null, 있다면 파라미터로 들어온 쿠키 이름과 동일한 쿠키가 있는지 찾고 리턴
private Cookie findCookie(HttpServletRequest request, String cookieName) {
if (request.getCookie() == null) {
return null;
}
return Arrays.stream(request.getCookies())
.filter(cookie -> cookie.getName().equals(cookieName))
.findAny()
.orElse(null);
}
}
우리가 직접 만든 세션 관리 기능을 실제 웹 애플리케이션에 적용해보면 아래와 같다.
아래 3개의 예제코드에서는
private final SessionManager sessionManager 메서드 밖에(클래스에) 선언되어 있다.
[예제코드 - 로그인(세션 생성)]
@PostMapping("/loing")
public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
//검증 로직 코드...
//로그인 로직 실행 후 로그인 성공된 Member 객체 생성
Member loginMember = loginService.login(form.getLoginId(), formgetPassword());
//로그인 실패 로직(loginMember==null)...
//로그인 성공 로직 + 세션 관리자를 통해 세션을 생성하고, 회원 데이터 보관
sessionManager.createSession(loginMember, response);
return "redirect:/";
}
로그인 성공 시, 세션을 등록하고 이때 생성된 세션아이디를 쿠키로 발행해 저장한다.
[예제 코드 - 로그아웃(세션 만료)]
@PostMapping("/logout")
public String logout(HttpServletRequest request) {
sessionManager.expirce(request);
return "redirect:/";
}
[예제 코드 - 홈 화면 이동(세션 조회)]
@GetMapping("/")
public String homeLogin(HttpServletRequest request, Model model) {
//세션 관리자에 저장된 회원 정보 조회
Member member = (Member)sessionManager.getSession(request);
if (member == null) {
return "home";
}
//로그인
model.addAttribute("member", member);
return "loginHome";
}
세션 관리자에서 저장된 회원 정보를 조회한다.
만약 회원 정보가 없으면, 쿠키나 세션이 없는 것이므로 로그인 되지 않은 것으로 처리합니다.
위의 예제들은 세션과 쿠키의 개념을 명확하게 이해하기 위해서 직접 만들어본것이다.
그런데 위 예제들을 보면 직접 개발하기에 코드가 복잡하고 불편할 것입니다.
그래서 서블릿(Servlet)도 세션 개념을 지원합니다.
로그인 처리하기 - 서블릿 HTTP 세션1
세션이라는 개념은 대부분의 웹 애플리케이션에 필요한 것입니다.
서블릿은 세션을 위해 HttpSession 이라는 기능을 제공하는데, 지금까지 나온 문제들을 해결해줍니다.
우리가 위에서 직접 구현해본 세션의 개념이 이미 구현되어 있고, 더 잘 구현되어 있습니다.
서블릿이 제공하는 HttpSession 도 결국 우리가 직접 만든 SessionManager 와 같은 방식으로 동작합니다.
서블릿을 통해 HttpSession 을 생성하면 다음과 같은 쿠키를 생성합니다.
쿠키 이름이 JSESSIONID 이고, 값은 추정 불가능한 랜덤 값입니다.
HttpSession 사용
서블릿이 제공하는 HttpSession 을 사용해봅시다.
우선 HttpSession 에 데이터를 보관하고 조회할 때, 같은 이름이 중복 되어 사용되기 때문에 상수 하나를 정의한다.
[예제 코드 - 세션 조회용 상수]
public class SessionConst {
public static final String LOGIN_MEMBER = "loginMember";
}
[예제 코드 - 로그인 컨트롤러]
@PostMapping("/login")
public String loginV2(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletRequest request) {
//LoginForm 에 대한 검증 로직
if(bindingResult.hasErrors()) {
return "login/loginForm";
}
//로그인 로직
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
//로그인 실패 시 로직
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
//아래 코드는 로그인 성공 처리 로직
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션에 로그인 회원 정보 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
@PostMapping("/logout")
public String logoutV2(HttpServletRequest request) {
//세션을 삭제한다.
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/";
}
세션 생성, 조회, 삭제
세션을 생성하려면 HttpSession의 getSession(true)를 사용하면 된다.
세션의 create 옵션
- request.getSession(true)
- 세션이 있으면 기존 세션을 반환한다.
- 세션이 없으면 새로운 세션을 생성해서 반환한다.
- reqeust.getSession(false)
- 세션이 있으면 기존 세션을 반환한다.
- 세션이 없으면 새로운 세션을 생성하지 않는다. null을 반환한다.
- request.getSession()
- 신규 세션을 생성하는 request.getSession(true) 와 동일하다.
세션에 로그인 회원 정보 보관
- session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
세션에 데이터를 보관하는 방법은 request.setAttribute(...)와 비슷하다.
하나의 세션에 여러 값을 저장할 수 있습니다.
세션 제거
- session.invalidate();
[예제 코드 - 홈 컨트롤러]
@GetMapping("/")
public String homeLoginV2(HttpServletRequest request, Model model) {
//세션이 없으면 home
HttpSession session = request.getSession(false);
if (session == null) {
return "home";
}
//로그인 시점에 세션에 보관한 회원 객체를 찾는다.
Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
위 코드에서 세션이 존재하는 지 확인하는 로직에서 request.getSession(false)를 사용한 이유는 request.getSession()를 사용하면 기본 값이 create: true 이므로, 로그인 하지 않을 사용자도 의미없는 세션이 만들어지기 때문입니다.
따라서 세션을 찾아서 사용하는 시점에는 create: faluse 옵션을 사용해서 세션을 생성하지 말아야합니다.
로그인 처리하기 - 서블릿 HTTP 세션2
스프링은 세션을 더 편리하게 사용할 수 있도록 @SessionAttribute 를 지원합니다.
이미 로그인 된 사용자를 찾을 때는 다음과 같이 사용하면 된다.
@SessionAttribute(name = "loginMember", required = false) Member loginMember
참고로 이 기능은 세션을 생성하지 않습니다.
[예제 코드 - 홈 컨트롤러]
@GetMapping("/")
public String homeLoginV3Spring(@SessionAttribute(name=SessionConst.LOGIN_MEMBER, required=false) Member loginMember, Model model) {
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
TrackingModes
위에서 소개한 방법들로 코드를 작성해 로그인을 처음 시도하면 URL이 다음과 같이 jsessionid 를 포함하는 것을 확인할 수 있습니다.
http://localhost:8080/;jsessionid=F59912412B34DR3F34324D1230.....
이것은 웹 브라우저가 쿠키를 지원하지 않을 때 쿠키 대신 URL을 통해 세션을 유지하는 방법입니다.
이 방법을 사용하려면 URL 에 이 값을 계속 포함해서 전달해야 합니다.
타임리프 같은 템플릿은 엔진을 통해서 링크를 걸면 jsessionid 를 URL 에 자동으로 포함해줍니다.
서버 입장에서는 웹 브라우저가 쿠키를 지원하는지 하지 않는지 최초에는 판단하지 못하므로, 쿠키 값도 전달하고, URL 에 jsessionid 도 함께 전달합니다.
URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶으면 스프링 설정 파일(application.properties)에 다음과 같은 설정을 추가해주면 됩니다.
server.servlet.session.tracking-modes=cookie
주의! jsessionid 가 URL에 있을 때 404 오류가 발생한다면..
스프링에서 최근 URL 매핑 전략이 변경 되었다고 합니다.
따라서 jsessionid 형식으로 출력될 때 컨트롤러를 찾지 못하고 404 오류가 발생할 수 있습니다.
해결 방안으로는 위에서 설명한 session.tracking-modes 를 사용하는 것입니다.
하지만 URL에 jsessionid가 꼭 필요하다면 스프링 설정 파일에 다음 옵션을 추가해주면 됩니다.
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
HttpSession에서 제공하는 정보
HttpSession에서는 다음과 같은 세션정보들을 제공합니다.
public void sessionInfo(HttpServletRequest request, String sessionId) {
HttpSession session = request.getSession(false);
log.info("sessionId={}", session.getId());
log.info("getMaxInactiveInterval={}", session.getMaxInactiveInterval());
log.info("creationTime={}", new Date(session.getCreationTime()));
log.info("lastAccessedTime={}", new Date(session.getLastAccessedTime()));
log.info("isNew={}", session.isNew());
}
- sessionId : 세션ID, JSESSIONID의 값이다. (ex. 545243B34FD02312F03045...)
- maxInactiveInterval : 세션의 유효 시간 (ex. 1800초, (30분))
- creationTime : 세션 생성일시
- lastAccessedTime : 세션과 연결된 사용자가 최근에 서버에 접근한 시간, 클라이언트에서 서버로 sessionId(JSESSIONID)를 요청한 경우에 갱신된다.
- isNew : 새로 생성된 세션인지, 아니면 이미 과거에 만들어졌고, 클라이언트에서 서버로 sessionId(JSESSIONID)를 요청해서 조회된 세션인지 여부
세션 타임아웃 설정 및 종료 시점
세션은 사용자가 로그아웃을 직접 호출해서 session.invalidate() 가 호출 되는 경우에 삭제됩니다.
그런데 대부분의 사용자들은 로그아웃을 선택하지 않고, 그냥 웹브라우저를 종료합니다.
문제는 HTTP가 비 연결성(ConnectionLess)이므로 서버 입장에서는 해당 사용자가 웹 브라우저를 종료한 것인지 아닌지를 인식할 수 없습니다. 따라서 서버에서 세션 데이터를 언제 삭제해야 하는지 판단하기가 어렵습니다.
그렇다고 세션을 무한정 유지되도록 한다면, 여러가지 문제가 발생합니다.
JSESSIONID를 탈취당한 경우 오랜 시간이 지나도 해당 쿠키로 악의적인 요청을 할 수 있습니다.
세션은 기본적으로 메모리에 생성되는데, 메모리의 크기가 무한하지 않기 때문에 꼭 필요한 경우만 생성해서 사용해야 합니다. 세션이 관리되지 않으면 성능저하가 발생하게 되고 OutOfMemoryException이 발생할 수 있습니다.
이러한 이유로 세션에는 타임아웃이 설정되어야 하는데, 단순하게 세션 생성 시점으로부터 30분 정도로 잡으면 될 것 같지만 문제는 30분이 지나면 세션이 삭제되기 때문에, 열심히 사이트를 돌아다니다가 또 로그인해서 세션을 생성해야하는 번거로움이 생깁니다.
더 나은 대안은 세션 생성 시점이 아니라 사용자가 서버에 최근에 요청한 시간을 기준으로 30분 정도를 유지해주는 것입니다. 이렇게 하면 사용자가 서비스를 사용하고 있으면, 세션의 생존 시간이 30분으로 계속 늘어나게 됩니다. 따라서 30분 마다 로그인해야 하는 번거로움이 사라지게 됩니다. HttpSession은 이 방식을 사용합니다.
스프링 부트에서 application.properties 에 글로벌 설정을 할 수 있습니다.
server.servlet.session.timeout=60 (60초, 기본은 1800초(30분))
글로벌 설정은 분 단위로 설정해야 합니다. 60(1분), 120(2분), ... 1800(30분) 등등
특정 세션 단위로 시간 설정하는 방법은 다음과 같습니다.
session.setMaxInactiveInterval(1800); //1800초
세션의 타임아웃 시간은 해당 세션과 관련된 JSESSIONID를 전달하는 HTTP 요청이 있으면 현재 시간으로 다시 초기화 됩니다. 이렇게 초기화 되면 세션 타임아웃으로 설정한 시간동안 세션을 추가로 사용할 수 있습니다.
sessionLgetLastAccessedTime() : 최근 세션 접근 시간
LastAccessedTime 이후로 timeout 시간이 지나면, WAS가 내부에서 해당 세션을 제거합니다.
정리
서블릿의 HttpSession이 제공하는 타임아웃 기능 덕분에 세션을 안전하고 편리하게 사용할 수 있습니다.
실무에서 주의할 점은 세션에는 최소한의 데이터만 보관해야 한다는 점입니다.
보관한 데이터 용량 * 사용자 수로 세션의 메모리 사용량이 급격하게 늘어나서 장애로 이어질 수 있습니다.
추가로 세션의 시간을 너무 길게 가져가면 메모리 사용이 계속 누적 될 수 있으므로 적당한 시간을 선택하는 것이 필요합니다.
참고(출처)
'Spring | Spring Boot' 카테고리의 다른 글
[Spring] 로그인 처리(2/2) - 필터(Filter)와 인터셉터(Interceptor) (0) | 2024.07.06 |
---|---|
[Spring] 메시지, 국제화 개념 및 적용방법 (0) | 2024.05.23 |
[Spring] 스프링 Redirect(리다이렉트)과 RedirectAttribute (0) | 2024.05.20 |
[Spring] HTTP 메시지 컨버터 이해쉽게 개념 정리 (0) | 2024.05.19 |
[Spring] 스프링MVC HTTP 응답 (0) | 2024.05.18 |
- Total
- Today
- Yesterday
- 인터페이스 추상클래스 비교
- RequiredArgsConstruct
- Spring
- Thymeleaf
- Servlet
- 타임리프
- 빈생명주기콜백
- 인식안됨
- HTTP요청
- 요청매핑
- 스프링 빈
- 객체지향설계원칙
- 스프링특징
- 스프링
- erd editor
- 인터페이스
- redirectattribute
- HttpServletRequest
- Overloding
- Java
- 스프링 컨테이너
- 타임리프 기본기능
- erd툴
- 요청데이터
- 인터페이스 추상클래스 차이
- 네이버지도크롤링
- 추상클래스
- 자바
- 크롤링
- 스프링http
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |