스터디 3주차 - 로그인 창 구현하기

유희원·2025년 3월 31일

멋쟁이사자처럼

목록 보기
3/5
post-thumbnail

1. 로그인 창 구현 방법 3가지

📌가. 세션 기반
세션 기반의 로그인 창에서는 서버가 세션 객체에 사용자 정보를 저장하고, 클라이언트는 쿠키를 통해 세션을 식별한다. 서버가 세션 상태를 관리하므로 보안에 좋으나, 메모리를 많이 사용한다.

쉽게 말한다면,🫡
-> 사용자가 로그인
-> 서버가 "이 사람 로그인했음" 이라고 장부에 기록
-> 사용자에게 들고다니라면서 세션 ID를 쥐어줌.
-> 후에 사용자가 기능을 요청하면 서버가 장부를 본 후 서비스를 제공

📌나. 쿠키 기반
쿠키 기반의 로그인 창에서는 사용자 정보를 직접 쿠키에 저장하고, 클라이언트가 직접 인증 정보를 계속 전송하는 방식을 사용한다. 이는 구현이 간단하나, 쿠키 탈취 시 보안에 위험이 있다는 단점이 있다.

쉽게 말한다면,🫡
-> 로그인하면 서버가 이름표(쿠키)를 만들어서 사용자에게 줌.
-> 이후에 서버에 요청할 때마다 이름표(쿠키)를 보여줌.
-> 서버는 이름표(쿠키)만을 보고 로그인했다고 판단.
-> 즉, 서버가 기억하는 게 아닌, 사용자가 직접 쿠키를 들고 다니며 증명하는 방식.

📌다. 무상태 (세션, 쿠키 둘 다 이용하지 않음)
세션과 쿠키를 둘 다 사용하지 않고 구현한 로그인 창에서는 매 요청마다 토큰이나 자격 정보를 같이 보내기 때문에 서버 확장에 유리하나 매 처리마자 인증 처리가 필요하다는 단점이 있다.

마찬가지로 쉽게 말한다면,🫡
-> 서버는 기억을 하지 않겠다고 선언. (강하게 표현하자면)
-> 대신 사용자는 로그인할 때 토큰이라는 보증서를 발급받음.
-> 이후 요청 시마다 사용자는 토큰이라는 보증서를 함께 제출함.
-> 서버는 토큰을 검사 후 유효하면 처리를 해줌.
-> 즉, 은행에서 거래할 때 신분증 지참을 하듯이 지니고 다니는 방식.

📌총 정리
세션 -> 서버가 기억함
쿠키 -> 사용자가 기억함
무상태 -> 사용자가 항상 증명함

2. 코드 설명

나는 위 3가지 방식 중 첫 번째인, "세션"을 이용한 로그인 창을 구현해보았다.

💡 요구사항
1. 아이디: 202301506
2. 비밀번호: printhaha
3. 로그인 성공하면 👉 /board/list로 이동 + "로그인 성공" 팝업
4. 로그인 실패하면 👉 "아이디 / 비밀번호가 틀렸습니다" 팝업 + 로그인창으로 다시 이동
5. 로그인 안하면 게시판 접근 불가

[1] LoginController.java - 로그인 요청 처리

@Controller
@RequestMapping("/board")
public class LoginController {

    @GetMapping("/login") // GET 요청이 /board/login 으로 들어오면
    public String loginForm() {
        return "login"; // 로그인 폼 화면을 보여줌
    }

    @PostMapping("/login") // 로그인 버튼 눌렀을 때
    public String login(@RequestParam("username") String username, // 입력한 아이디
                        @RequestParam("password") String password, // 입력한 비밀번호
                        HttpServletRequest request,
                        RedirectAttributes redirectAttributes) {

        // 아이디와 비밀번호가 맞는지 확인
        if ("202301506".equals(username) && "printhaha".equals(password)) {
            // 세션 가져오기 (없으면 새로 만듦)
            HttpSession session = request.getSession();
            // 세션에 사용자 정보 저장
            session.setAttribute("userId", username);
            // 로그인 성공 메시지 넘기기
            redirectAttributes.addFlashAttribute("message", "로그인에 성공했습니다.");
            // 게시글 리스트로 이동
            return "redirect:/board/list";
        } else {
            // 로그인 실패 메시지 넘기기
            redirectAttributes.addFlashAttribute("error", "유효하지 않은 아이디 / 비번입니다. 다시 입력하세요.");
            // 다시 로그인 페이지로 이동
            return "redirect:/board/login";
        }
    }
}

[2] LoginInterceptor.java - 로그인 여부 체크(세션 검사)

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 세션이 있는지 확인 (없으면 null)
        HttpSession session = request.getSession(false);

        // 로그인 안했으면
        if (session == null || session.getAttribute("userId") == null) {
            // 로그인 페이지로 강제 이동
            response.sendRedirect("/board/login");
            return false;
        }
        return true; // 로그인 되어있으면 계속 진행
    }
}

[3] login.html & [4] boardlist.html

 // 로그인 실패 시 alert 창
    <script th:if="${error}" th:inline="javascript">
        alert([[${error}]]);
    </script>
</body>
</html>

// 로그인 성공 시 alert
<div th:if="${message}">
    <script th:inline="javascript">
        alert([[${message}]]);
    </script>
</div>

3. 결과

로그인 창 첫 화면

로그인 성공 시(팝업 메세지 창 후 리스트 창으로 이동)

로그인 실패 시(팝업 메세지 창 후 다시 로그인 화면으로 이동)

profile
똑똑한 망치

2개의 댓글

comment-user-thumbnail
2025년 4월 1일

로그인창 구현 방식이 여러 종류가 있는 지는 몰랐네요.. 정리해 주셔서 감사합니다!

답글 달기
comment-user-thumbnail
2025년 4월 1일

로그인 방식을 예시를 들어 설명해줘서 이해하기 좋았습니다. 그럼 무상태 안에 jwt 방식이 포함되어 있는건가여??

답글 달기