first commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package com.paynuri.common.interceptor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* 로깅 인터셉터 예제
|
||||
* 필요시 WebConfig에서 등록하여 사용
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LoggingInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
log.debug("[preHandle][{}] {}", request.getMethod(), request.getRequestURI());
|
||||
long startTime = System.currentTimeMillis();
|
||||
request.setAttribute("startTime", startTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
|
||||
log.debug("[postHandle][{}] {}", request.getMethod(), request.getRequestURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
long startTime = (Long) request.getAttribute("startTime");
|
||||
long endTime = System.currentTimeMillis();
|
||||
long executeTime = endTime - startTime;
|
||||
|
||||
log.debug("[afterCompletion][{}] {} - {}ms", request.getMethod(), request.getRequestURI(), executeTime);
|
||||
|
||||
if (ex != null) {
|
||||
log.error("[afterCompletion] Exception: ", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/main/java/com/paynuri/config/RedisSessionConfig.java
Normal file
69
src/main/java/com/paynuri/config/RedisSessionConfig.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.paynuri.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.DefaultCookieSerializer;
|
||||
|
||||
/**
|
||||
* Redis Session Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@EnableRedisHttpSession
|
||||
public class RedisSessionConfig {
|
||||
|
||||
/**
|
||||
* Development Profile Session Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("dev")
|
||||
@EnableRedisHttpSession(redisNamespace = "spring:session:dev")
|
||||
static class DevRedisSessionConfig {
|
||||
@Bean
|
||||
public CookieSerializer cookieSerializer() {
|
||||
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
|
||||
serializer.setCookieName("DEV_SESSION");
|
||||
serializer.setCookiePath("/");
|
||||
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Profile Session Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("test")
|
||||
@EnableRedisHttpSession(redisNamespace = "spring:session:test")
|
||||
static class TestRedisSessionConfig {
|
||||
@Bean
|
||||
public CookieSerializer cookieSerializer() {
|
||||
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
|
||||
serializer.setCookieName("TEST_SESSION");
|
||||
serializer.setCookiePath("/");
|
||||
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Production Profile Session Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("real")
|
||||
@EnableRedisHttpSession(redisNamespace = "spring:session:real")
|
||||
static class RealRedisSessionConfig {
|
||||
@Bean
|
||||
public CookieSerializer cookieSerializer() {
|
||||
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
|
||||
serializer.setCookieName("SESSION");
|
||||
serializer.setCookiePath("/");
|
||||
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
|
||||
serializer.setUseHttpOnlyCookie(true);
|
||||
serializer.setUseSecureCookie(true);
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/paynuri/config/SecurityConfig.java
Normal file
40
src/main/java/com/paynuri/config/SecurityConfig.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.paynuri.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;
|
||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// CSRF 설정
|
||||
.csrf(csrf -> csrf
|
||||
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||||
)
|
||||
// 요청 인증 설정
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.requestMatchers("/css/**", "/js/**", "/images/**", "/favicon.ico").permitAll()
|
||||
.requestMatchers("/", "/home", "/public/**").permitAll()
|
||||
.requestMatchers("/session/**", "/session").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
// 로그인 설정
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
.permitAll()
|
||||
)
|
||||
// 로그아웃 설정
|
||||
.logout(logout -> logout
|
||||
.permitAll()
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/paynuri/config/WebConfig.java
Normal file
36
src/main/java/com/paynuri/config/WebConfig.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.paynuri.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 정적 리소스 핸들러 설정
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/static/**")
|
||||
.addResourceLocations("classpath:/static/")
|
||||
.setCachePeriod(3600);
|
||||
|
||||
registry.addResourceHandler("/uploads/**")
|
||||
.addResourceLocations("file:uploads/")
|
||||
.setCachePeriod(3600);
|
||||
}
|
||||
/**
|
||||
* Interceptor 설정
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 필요한 경우 Interceptor 추가
|
||||
// registry.addInterceptor(new LoggingInterceptor())
|
||||
// .addPathPatterns("/**")
|
||||
// .excludePathPatterns("/static/**", "/css/**", "/js/**", "/images/**");
|
||||
}
|
||||
}
|
||||
42
src/main/java/com/paynuri/www/SessionController.java
Normal file
42
src/main/java/com/paynuri/www/SessionController.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.paynuri.www;
|
||||
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
public class SessionController {
|
||||
|
||||
@GetMapping("/session")
|
||||
public String sessionPage(HttpSession session, Model model) {
|
||||
String username = (String) session.getAttribute("username");
|
||||
Integer visitCount = (Integer) session.getAttribute("visitCount");
|
||||
|
||||
if (visitCount == null) {
|
||||
visitCount = 0;
|
||||
}
|
||||
visitCount++;
|
||||
session.setAttribute("visitCount", visitCount);
|
||||
|
||||
model.addAttribute("username", username);
|
||||
model.addAttribute("visitCount", visitCount);
|
||||
model.addAttribute("sessionId", session.getId());
|
||||
|
||||
return "session-example";
|
||||
}
|
||||
|
||||
@PostMapping("/session/login")
|
||||
public String login(@RequestParam String username, HttpSession session) {
|
||||
session.setAttribute("username", username);
|
||||
return "redirect:/session";
|
||||
}
|
||||
|
||||
@PostMapping("/session/logout")
|
||||
public String logout(HttpSession session) {
|
||||
session.invalidate();
|
||||
return "redirect:/session";
|
||||
}
|
||||
}
|
||||
13
src/main/java/com/paynuri/www/WwwApplication.java
Normal file
13
src/main/java/com/paynuri/www/WwwApplication.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.paynuri.www;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class WwwApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WwwApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/resources/application-dev.properties
Normal file
31
src/main/resources/application-dev.properties
Normal file
@@ -0,0 +1,31 @@
|
||||
# Development Profile
|
||||
server.port=8080
|
||||
|
||||
# Logging
|
||||
logging.level.root=INFO
|
||||
logging.level.com.paynuri.www=DEBUG
|
||||
|
||||
# Redis Session
|
||||
spring.data.redis.host=localhost
|
||||
spring.data.redis.port=6379
|
||||
spring.data.redis.password=
|
||||
spring.session.store-type=redis
|
||||
spring.session.timeout=1800s
|
||||
|
||||
# Database (H2 example)
|
||||
# spring.datasource.url=jdbc:h2:mem:devdb
|
||||
# spring.datasource.driver-class-name=org.h2.Driver
|
||||
# spring.datasource.username=sa
|
||||
# spring.datasource.password=
|
||||
|
||||
# Database with Log4jdbc (H2 example)
|
||||
# spring.datasource.url=jdbc:log4jdbc:h2:mem:devdb
|
||||
# spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
# spring.datasource.username=sa
|
||||
# spring.datasource.password=
|
||||
|
||||
# Database with Log4jdbc (MariaDB example)
|
||||
# spring.datasource.url=jdbc:log4jdbc:mariadb://localhost:3306/devdb?useUnicode=true&characterEncoding=utf8
|
||||
# spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
# spring.datasource.username=devuser
|
||||
# spring.datasource.password=devpass
|
||||
23
src/main/resources/application-real.properties
Normal file
23
src/main/resources/application-real.properties
Normal file
@@ -0,0 +1,23 @@
|
||||
# Production Profile
|
||||
server.port=80
|
||||
|
||||
# Logging
|
||||
logging.level.root=WARN
|
||||
logging.level.com.paynuri.www=INFO
|
||||
|
||||
# Redis Session
|
||||
spring.data.redis.host=localhost
|
||||
spring.data.redis.port=6379
|
||||
spring.data.redis.password=
|
||||
spring.session.store-type=redis
|
||||
spring.session.timeout=3600s
|
||||
# Redis Connection Pool (Production)
|
||||
spring.data.redis.lettuce.pool.max-active=10
|
||||
spring.data.redis.lettuce.pool.max-idle=5
|
||||
spring.data.redis.lettuce.pool.min-idle=2
|
||||
|
||||
# Database (Production DB example)
|
||||
# spring.datasource.url=jdbc:mysql://localhost:3306/prod_db
|
||||
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
# spring.datasource.username=prod_user
|
||||
# spring.datasource.password=prod_password
|
||||
31
src/main/resources/application-test.properties
Normal file
31
src/main/resources/application-test.properties
Normal file
@@ -0,0 +1,31 @@
|
||||
# Test Profile
|
||||
server.port=8081
|
||||
|
||||
# Logging
|
||||
logging.level.root=INFO
|
||||
logging.level.com.paynuri.www=DEBUG
|
||||
|
||||
# Redis Session
|
||||
spring.data.redis.host=localhost
|
||||
spring.data.redis.port=6379
|
||||
spring.data.redis.password=
|
||||
spring.session.store-type=redis
|
||||
spring.session.timeout=1800s
|
||||
|
||||
# Database (Test DB example)
|
||||
# spring.datasource.url=jdbc:h2:mem:testdb
|
||||
# spring.datasource.driver-class-name=org.h2.Driver
|
||||
# spring.datasource.username=sa
|
||||
# spring.datasource.password=
|
||||
|
||||
# Database with Log4jdbc (H2 example)
|
||||
# spring.datasource.url=jdbc:log4jdbc:h2:mem:testdb
|
||||
# spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
# spring.datasource.username=sa
|
||||
# spring.datasource.password=
|
||||
|
||||
# Database with Log4jdbc (MariaDB example)
|
||||
# spring.datasource.url=jdbc:log4jdbc:mariadb://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8
|
||||
# spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
# spring.datasource.username=testuser
|
||||
# spring.datasource.password=testpass
|
||||
4
src/main/resources/application.properties
Normal file
4
src/main/resources/application.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
spring.application.name=www
|
||||
|
||||
# Active Profile (dev, test, real)
|
||||
spring.profiles.active=dev
|
||||
108
src/main/resources/log4j2-spring.xml
Normal file
108
src/main/resources/log4j2-spring.xml
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Properties>
|
||||
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Property>
|
||||
<Property name="LOG_DIR">logs</Property>
|
||||
</Properties>
|
||||
|
||||
<Appenders>
|
||||
<!-- Console Appender -->
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||
</Console>
|
||||
|
||||
<!-- File Appender -->
|
||||
<RollingFile name="RollingFile" fileName="${LOG_DIR}/application.log"
|
||||
filePattern="${LOG_DIR}/application-%d{yyyy-MM-dd}-%i.log">
|
||||
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
||||
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="10"/>
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<!-- Spring Framework Logger -->
|
||||
<Logger name="org.springframework" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Logger>
|
||||
|
||||
<!-- Development Profile -->
|
||||
<SpringProfile name="dev">
|
||||
<Logger name="com.paynuri.www" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Logger>
|
||||
<!-- JDBC SQL Logging -->
|
||||
<Logger name="jdbc.sqlonly" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="jdbc.sqltiming" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="jdbc.resultsettable" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="jdbc.audit" level="off"/>
|
||||
<Logger name="jdbc.resultset" level="off"/>
|
||||
<Logger name="jdbc.connection" level="off"/>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Root>
|
||||
</SpringProfile>
|
||||
|
||||
<!-- Test Profile -->
|
||||
<SpringProfile name="test">
|
||||
<Logger name="com.paynuri.www" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Logger>
|
||||
<!-- JDBC SQL Logging -->
|
||||
<Logger name="jdbc.sqlonly" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="jdbc.sqltiming" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="jdbc.resultsettable" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="jdbc.audit" level="off"/>
|
||||
<Logger name="jdbc.resultset" level="off"/>
|
||||
<Logger name="jdbc.connection" level="off"/>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Root>
|
||||
</SpringProfile>
|
||||
|
||||
<!-- Production Profile -->
|
||||
<SpringProfile name="real">
|
||||
<Logger name="com.paynuri.www" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Logger>
|
||||
<Root level="warn">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Root>
|
||||
</SpringProfile>
|
||||
|
||||
<!-- Default (no profile) -->
|
||||
<SpringProfile name="!dev & !test & !real">
|
||||
<Logger name="com.paynuri.www" level="info" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Logger>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="RollingFile"/>
|
||||
</Root>
|
||||
</SpringProfile>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
2
src/main/resources/log4jdbc.log4j2.properties
Normal file
2
src/main/resources/log4jdbc.log4j2.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
|
||||
log4jdbc.dump.sql.maxlinelength=0
|
||||
53
src/main/resources/templates/csrf-example.html
Normal file
53
src/main/resources/templates/csrf-example.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CSRF Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CSRF 토큰 예제</h1>
|
||||
|
||||
<!-- Thymeleaf에서 자동으로 CSRF 토큰을 포함 -->
|
||||
<form th:action="@{/submit}" method="post">
|
||||
<input type="text" name="data" placeholder="데이터 입력">
|
||||
<button type="submit">전송</button>
|
||||
<!-- CSRF 토큰은 자동으로 추가됩니다 -->
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- 수동으로 CSRF 토큰 추가 (필요한 경우) -->
|
||||
<form action="/manual-submit" method="post">
|
||||
<input type="text" name="data" placeholder="데이터 입력">
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
|
||||
<button type="submit">전송 (수동 CSRF)</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- JavaScript에서 CSRF 토큰 사용 -->
|
||||
<div>
|
||||
<button onclick="ajaxSubmit()">AJAX 전송</button>
|
||||
</div>
|
||||
|
||||
<script th:inline="javascript">
|
||||
const csrfToken = /*[[${_csrf.token}]]*/ '';
|
||||
const csrfHeader = /*[[${_csrf.headerName}]]*/ '';
|
||||
|
||||
function ajaxSubmit() {
|
||||
fetch('/api/data', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
[csrfHeader]: csrfToken
|
||||
},
|
||||
body: JSON.stringify({ data: 'example' })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => console.log(data))
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
45
src/main/resources/templates/session-example.html
Normal file
45
src/main/resources/templates/session-example.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Redis Session Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Redis Session 예제</h1>
|
||||
|
||||
<div>
|
||||
<p><strong>Session ID:</strong> <span th:text="${sessionId}">N/A</span></p>
|
||||
<p><strong>방문 횟수:</strong> <span th:text="${visitCount}">0</span></p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div th:if="${username == null}">
|
||||
<h2>로그인</h2>
|
||||
<form th:action="@{/session/login}" method="post">
|
||||
<input type="text" name="username" placeholder="사용자명" required>
|
||||
<button type="submit">로그인</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div th:if="${username != null}">
|
||||
<h2>환영합니다!</h2>
|
||||
<p><strong>사용자:</strong> <span th:text="${username}">Guest</span></p>
|
||||
<form th:action="@{/session/logout}" method="post">
|
||||
<button type="submit">로그아웃</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div>
|
||||
<h3>Redis Session 정보</h3>
|
||||
<ul>
|
||||
<li>세션은 Redis에 저장됩니다</li>
|
||||
<li>애플리케이션 재시작 후에도 세션이 유지됩니다</li>
|
||||
<li>여러 서버 간 세션 공유가 가능합니다</li>
|
||||
<li>프로파일별로 다른 namespace를 사용합니다</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user