Spring Boot와 Vue.js를 사용하는 애플리케이션에서 세션 관리 및 로그아웃 기능을 구현하면서 간단한 내용을 정리해본다.
로그인/로그아웃 기능은 프로젝트 초반에 만들고 이후에 진행하면서 몇 가지 옵션들을 추가하는 형식으로 진행되는데 한창 진행할때는 전체적인 구조와 개념들이 머리 속에 있는 듯 하지만 다른 프로젝트를 시작할때는 기억이 잘 안나서 다시 찾아보곤 하게 된다.
아주 간단한 기본 구조를 기록으로 남기고, 이후 내용은 프로젝트 특성에 따라 가감해서 사용하도록 한다.
1. 백엔드에서 세션 기반 인증 설정
1.1 Spring Security 추가
build.gradle.kts
에 Spring Security 의존성을 추가한다.
dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
// 기타 다른 의존성들....
}
YAML1.2 기본 Security 설정
Spring Security를 사용하여 로그인과 세션 관리 설정을 추가합니다.
SecurityConfig.kt
package com.example.demo.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.web.SecurityFilterChain
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { csrf ->
csrf.disable() // 개발환경 CSRF 비활성화
}
.authorizeHttpRequests { requests ->
requests
.requestMatchers("/login", "/api/public/**").permitAll() // 로그인 화면 및 공개 API는 허용
.anyRequest().authenticated() // 나머지는 인증 필요
}
.formLogin { formLogin ->
formLogin
.loginPage("/login") // 로그인 페이지 설정
.permitAll()
}
.logout { logout ->
logout
.logoutUrl("/logout") // 로그아웃 URL
.logoutSuccessUrl("/login") // 로그아웃 성공 후 이동할 페이지
.invalidateHttpSession(true) // 세션 무효화
.deleteCookies("JSESSIONID") // 쿠키 삭제
}
return http.build()
}
}
Kotlin1.3 컨트롤러에서 로그인 성공 후 세션 설정
로그인 후 사용자의 세션 정보를 저장하도록 구현한다.
LoginController.kt
package com.example.demo.controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
import jakarta.servlet.http.HttpSession
data class LoginRequest(val username: String, val password: String)
@RestController
class LoginController {
@PostMapping("/api/login")
fun login(@RequestBody request: LoginRequest, session: HttpSession): String {
// 로그인 검증 로직 (간단한 예제)
if (request.username == "admin" && request.password == "password") {
session.setAttribute("username", request.username) // 세션에 사용자 저장
return "Login successful"
}
throw RuntimeException("Invalid credentials")
}
@GetMapping("/api/logout")
fun logout(session: HttpSession): String {
session.invalidate() // 세션 무효화
return "Logged out successfully"
}
}
Kotlin2. 백엔드 인증 상태 확인 API
Vue.js에서 사용자의 로그인 상태를 확인하기 위해 종종 사용하게 되는 상태 인증용 API를 작성한다.
AuthController.kt
package com.example.demo.controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import javax.servlet.http.HttpSession
@RestController
class AuthController {
@GetMapping("/api/auth/status")
fun authStatus(session: HttpSession): Boolean {
return session.getAttribute("username") != null // 세션에 사용자가 있으면 true
}
}
Kotlin3. 프론트엔드에서 인증 관리
3.1 Vuex 로그인 상태 관리
로그인 성공 여부를 저장하여 사용한다.
store.js
import { reactive } from "vue";
export const store = reactive({
isAuthenticated: false,
setAuthenticated(status) {
this.isAuthenticated = status;
},
});
Kotlin3.2 인증 상태 확인
main.js
또는 각 라우트 전환 시 인증 상태를 확인하여 필요한 경우 로그인으로 유도한다.
라우터 가드 설정
import { createRouter, createWebHistory } from "vue-router";
import { store } from "./store";
import LoginView from "@/views/LoginView.vue";
import LogListView from "@/views/LogListView.vue";
const routes = [
{ path: "/login", name: "Login", component: LoginView },
{
path: "/logs",
name: "Logs",
component: LogListView,
meta: { requiresAuth: true }, // 인증이 필요한 경로
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAuth) {
// 인증 상태 확인
const response = await fetch("/api/auth/status");
const isAuthenticated = await response.json();
if (isAuthenticated) {
store.setAuthenticated(true);
next();
} else {
store.setAuthenticated(false);
next("/login");
}
} else {
next();
}
});
export default router;
3.3 로그인 및 로그아웃
LoginView.vue
<template>
<div>
<h1>로그인</h1>
<input v-model="username" placeholder="아이디" />
<input v-model="password" type="password" placeholder="비밀번호" />
<button @click="login">로그인</button>
</div>
</template>
<script>
export default {
data() {
return {
username: "",
password: "",
};
},
methods: {
async login() {
const response = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: this.username, password: this.password }),
});
if (response.ok) {
this.$router.push("/logs");
} else {
alert("로그인 실패!");
}
},
},
};
</script>
Logout Button
<template>
<button @click="logout">로그아웃</button>
</template>
<script>
export default {
methods: {
async logout() {
await fetch("/api/logout");
this.$router.push("/login");
},
},
};
</script>
4. 내용 정리
- 로그인 필요
- 인증이 필요한 경로로 접근 시, 로그인하지 않았다면
/login
화면으로 리다이렉트된다.
- 인증이 필요한 경로로 접근 시, 로그인하지 않았다면
- 로그인 성공
- 로그인 시 세션에 사용자 정보가 저장되고, 인증 상태가 업데이트된다.
- 로그아웃
- 로그아웃 시 세션이 무효화되며,
/login
화면으로 이동한다.
- 로그아웃 시 세션이 무효화되며,
기본적인 로그인/로그아웃 구성을 마치고 비즈니스 로직 개발에 일단 집중하자. Security 관련된 정책은 이후에도 변경되는 부분이 종종 생기기 때문에 처음에는 아주 간단한 구조만 준비하고 진행하면서 필요한 경우 적절히 추가하는 식으로 진행한다.