Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
280dc64
feat(web-view): DynamicViewResponse 개발
codingbaraGo Jan 13, 2026
44f875b
enhance(auth): AuthenticationInfo에 Attribute 추가
codingbaraGo Jan 13, 2026
f8cddbe
feat(web-view): View template engine 개발
codingbaraGo Jan 13, 2026
04c24e6
feat(web-view): 동적 뷰 렌더러 개발
codingbaraGo Jan 13, 2026
1388cf6
feat(app-handler): 기본 페이지 핸들러 개발
codingbaraGo Jan 13, 2026
b5b90dc
feat(view): 기본 페이지 수정
codingbaraGo Jan 13, 2026
76c85c6
chore: 코드 정리
codingbaraGo Jan 13, 2026
428f758
chore(test): 기본 페이지 이동으로 인한 변경사항 반영
codingbaraGo Jan 13, 2026
e4ec9a7
fix: 코드리뷰 반영
codingbaraGo Jan 13, 2026
c426f09
Merge pull request #53 from codingbaraGo/feat/web/dynamic-view#23 (se…
codingbaraGo Jan 13, 2026
31b0f9b
feat(auth-logout): 로그아웃 핸들러 개발
codingbaraGo Jan 13, 2026
d0750a9
feat(view): 공통 헤더에 로그아웃 버튼 추가
codingbaraGo Jan 13, 2026
b6d60ba
feat(http): HttpResponse에 redirect 설정 메서드 추가
codingbaraGo Jan 13, 2026
a4d038f
Merge pull request #55 from codingbaraGo/feat/authentication/logout/#…
codingbaraGo Jan 13, 2026
4ab02e3
feat(web-handler): 뷰 기본 핸들러 개발
codingbaraGo Jan 13, 2026
579ce4d
refactor(app-handler): 로그인 핸들러 반환 타입 수정: StaticViewResponse -> Redire…
codingbaraGo Jan 13, 2026
325103b
feat(view): 공통 에러 팝업 개발
codingbaraGo Jan 13, 2026
f3d71c9
refactor(view): 헤더 버튼 추가
codingbaraGo Jan 13, 2026
c7b88d9
refactor(view): 정적 뷰 수정
codingbaraGo Jan 13, 2026
b04c211
fix(view): 에러 팝업 로직 수정 (https://github.com/codingbaraGo/be-was/pull/5…
codingbaraGo Jan 13, 2026
4e150b5
Merge pull request #58 from codingbaraGo/enhance/view/#56 (see PR #57)
codingbaraGo Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/main/java/app/handler/HomeHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package app.handler;

import http.HttpMethod;
import http.HttpStatus;
import http.request.HttpRequest;
import web.handler.SingleArgHandler;
import web.response.DynamicViewResponse;
import web.response.HandlerResponse;

public class HomeHandler extends SingleArgHandler<HttpRequest> {

public HomeHandler() {
super(HttpMethod.GET, "/");
}

@Override
public HandlerResponse handle(HttpRequest request) {
return DynamicViewResponse.of(HttpStatus.OK, "/index.html");
}
}
9 changes: 5 additions & 4 deletions src/main/java/app/handler/LoginWithPost.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,17 @@ public HandlerResponse handle(QueryParameters params) {

SessionEntity session = sessionManager.create(
user.getUserId(),
user.getUserRole());
user.getUserRole(),
user.getNickname());

RedirectResponse res = RedirectResponse.to("/");
res.setCookie(
RedirectResponse response = RedirectResponse.to("/");
response.setCookie(
CookieBuilder.of("SID", session.getId())
.path("/")
.httpOnly()
.sameSite(CookieBuilder.SameSite.LAX)
.maxAge(VariableConfig.ABSOLUTE_MS)
);
return res;
return response;
}
}
28 changes: 28 additions & 0 deletions src/main/java/app/handler/LogoutWithPost.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package app.handler;

import http.HttpMethod;
import http.request.HttpRequest;
import http.response.CookieBuilder;
import web.handler.SingleArgHandler;
import web.response.HandlerResponse;
import web.response.RedirectResponse;
import web.session.SessionStorage;

public class LogoutWithPost extends SingleArgHandler<HttpRequest> {
private final SessionStorage sessionManager;

public LogoutWithPost(SessionStorage sessionManager) {
super(HttpMethod.POST, "/user/logout");
this.sessionManager = sessionManager;
}

@Override
public HandlerResponse handle(HttpRequest request) {
String sid = request.getCookieValue("SID").orElse(null);
if (sid != null) sessionManager.invalidate(sid);

RedirectResponse response = RedirectResponse.to("/");
response.setCookie(CookieBuilder.delete("SID").path("/"));
return response;
}
}
4 changes: 2 additions & 2 deletions src/main/java/app/handler/RegisterWithPost.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import web.filter.authentication.UserRole;
import web.handler.SingleArgHandler;
import web.response.HandlerResponse;
import web.response.StaticViewResponse;
import web.response.RedirectResponse;

public class RegisterWithPost extends SingleArgHandler<QueryParameters> {
private static final Logger log = LoggerFactory.getLogger(RegisterWithPost.class);
Expand All @@ -27,6 +27,6 @@ public HandlerResponse handle(QueryParameters params) {
String password = params.getQueryValue("password").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "password required"));
Database.addUser(new User(password, nickname, email, UserRole.MEMBER.toString()));
log.info("Registered - password:{}, nickname:{}, email:{}", password, nickname, email);
return StaticViewResponse.of("/login");
return RedirectResponse.to("/login");
}
}
50 changes: 43 additions & 7 deletions src/main/java/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package config;

import app.handler.LoginWithPost;
import app.handler.RegisterWithGet;
import app.handler.RegisterWithPost;
import app.handler.*;
import exception.ExceptionHandlerMapping;
import exception.handler.ErrorExceptionHandler;
import exception.handler.ServiceExceptionHandler;
Expand All @@ -20,12 +18,17 @@
import web.dispatch.argument.resolver.HttpRequestResolver;
import web.dispatch.argument.resolver.QueryParamsResolver;
import web.filter.*;
import web.handler.DefaultViewHandler;
import web.handler.StaticContentHandler;
import web.handler.WebHandler;
import web.renderer.DynamicViewRenderer;
import web.renderer.HttpResponseRenderer;
import web.renderer.RedirectRenderer;
import web.renderer.StaticViewRenderer;
import web.renderer.view.ExpressionResolver;
import web.session.SessionStorage;
import web.renderer.view.TemplateEngine;
import web.renderer.view.TemplateLoader;

import java.util.List;

Expand Down Expand Up @@ -84,8 +87,10 @@ public List<WebHandler> webHandlerList() {
staticContentHandler(),
registerWithGet(),
registerWithPost(),
loginWithPost()
)
loginWithPost(),
logoutWithPost(),
homeHandler(),
defaultViewHandler())
);
}

Expand All @@ -96,6 +101,10 @@ public StaticContentHandler staticContentHandler() {
);
}

public DefaultViewHandler defaultViewHandler(){
return getOrCreate("defaultViewHandler", DefaultViewHandler::new);
}

public RegisterWithGet registerWithGet() {
return getOrCreate(
"registerWithGet",
Expand All @@ -115,13 +124,23 @@ public LoginWithPost loginWithPost() {
() -> new LoginWithPost(sessionStorage()));
}

public LogoutWithPost logoutWithPost(){
return getOrCreate("logoutWithPost",
() -> new LogoutWithPost(sessionStorage()));
}

public HomeHandler homeHandler(){
return getOrCreate("homeHandler", HomeHandler::new);
}

// ===== Renderer =====
public List<HttpResponseRenderer> httpResponseRendererList() {
return getOrCreate(
"httpResponseRendererList",
() -> List.of(
staticViewRenderer(),
redirectRenderer()
redirectRenderer(),
dynamicViewRenderer()
)
);
}
Expand All @@ -137,6 +156,24 @@ public RedirectRenderer redirectRenderer() {
return getOrCreate("redirectRenderer", RedirectRenderer::new);
}

public DynamicViewRenderer dynamicViewRenderer() {
return getOrCreate("dynamicViewRenderer",
() -> new DynamicViewRenderer(templateEngine()));
}

// ===== ViewEngine =====
public TemplateEngine templateEngine() {
return getOrCreate("templateEngine",
() -> new TemplateEngine(templateLoader(), expressionResolver()));
}

public ExpressionResolver expressionResolver(){
return getOrCreate("expressResolver", ExpressionResolver::new);
}

public TemplateLoader templateLoader() {
return getOrCreate("templateLoader", TemplateLoader::new);
}

// ===== Adapter =====
public List<HandlerAdapter> handlerAdapterList() {
Expand Down Expand Up @@ -164,7 +201,6 @@ public DefaultHandlerAdapter defaultHandlerAdapter() {
DefaultHandlerAdapter::new
);
}

// ===== Resolver =====
public List<ArgumentResolver<?>> argumentResolverList() {
return getOrCreate(
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import exception.ErrorException;

import java.util.List;

public class SecurityConfig extends SingletonContainer {
private final AppConfig appConfig = new AppConfig();
private int callCount;
Expand All @@ -15,9 +17,9 @@ public void config(){
public void setPaths(){
appConfig.filterChainContainer()
.addPath(FilterType.AUTHENTICATED, "/mypage/**")
.addPath(FilterType.LOG_IN, "/user/login")
.addPath(FilterType.ALL, "/user/**")
.addPath(FilterType.PUBLIC, "/**");
.addPaths(FilterType.LOG_IN, List.of("/user/login", "/login"))
.addPaths(FilterType.PUBLIC, List.of("/", "/home/*"))
.addPath(FilterType.ALL, "/**");
}


Expand Down
3 changes: 3 additions & 0 deletions src/main/java/config/VariableConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ public class VariableConfig {
public static final List<String> STATIC_RESOURCE_ROOTS = List.of(
"./src/main/resources",
"./src/main/resources/static");
public static final List<String> DYNAMIC_RESOURCE_ROOTS = List.of(
"./src/main/resources/templates"
);

public static final long IDLE_MS = 30*60*100;
public static final long ABSOLUTE_MS = 180*60*100;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/exception/handler/ErrorExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.time.format.DateTimeFormatter;

public class ErrorExceptionHandler implements ExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(ErrorException.class);
private final Logger logger = LoggerFactory.getLogger(ErrorExceptionHandler.class);
@Override
public boolean support(Throwable e) {
return e instanceof ErrorException;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/http/response/HttpResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ public void setBody(File file, byte[] body) {
setHeader("Content-Length", String.valueOf(body.length));
}

public void redirectTo(String path){
setStatus(HttpStatus.FOUND);
setHeader("Location", path);
setHeader("Content-Length", "0");
}

private String guessContentType(File file) {
String byName = URLConnection.guessContentTypeFromName(file.getName());
if (byName != null) return byName;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/web/dispatch/Dispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public HttpResponse handle(HttpRequest request, HttpResponse response){
HandlerAdapter adapter = adapterList.stream().filter(ha -> ha.support(handler))
.findFirst().orElseThrow(() -> new ErrorException("DispatcherError: No adapter matched"));

HandlerResponse handlerResponse = adapter.handle(request, handler);
HandlerResponse handlerResponse = adapter.handle(request, handler).postHandling(request, response);

HttpResponseRenderer responseHandler = responseHandlerList.stream()
.filter(rh -> rh.supports(handlerResponse))
Expand Down
1 change: 1 addition & 0 deletions src/main/java/web/filter/AccessLogFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class AccessLogFilter implements ServletFilter {

@Override
public void runFilter(HttpRequest request, HttpResponse response, FilterChainContainer.FilterChainEngine chain) {
request.getOrGenerateRid();
chain.doFilter();
if(request.getAuthenticationInfo()!=null &&
request.getAuthenticationInfo().getRole().equals(UserRole.MEMBER)) {
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/web/filter/AuthenticationFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import http.HttpStatus;
import http.request.HttpRequest;
import http.response.HttpResponse;
import web.filter.authentication.AuthenticationInfo;
import web.filter.authentication.UnanimousAuthentication;
import web.filter.authentication.UserAuthentication;
import web.filter.authentication.UserRole;
Expand All @@ -20,17 +21,17 @@ public AuthenticationFilter(SessionStorage sessionManager) {
public void runFilter(HttpRequest request, HttpResponse response, FilterChainContainer.FilterChainEngine chain) {
String sid = request.getCookieValue("SID").orElse(null);
SessionEntity session = sessionManager.getValid(sid);
AuthenticationInfo authInfo;

if (session == null) {
request.setAuthenticationInfo(
UnanimousAuthentication.of());
authInfo = UnanimousAuthentication.of();
} else{
request.setAuthenticationInfo(
UserAuthentication.of(
session.getUserId(),
UserRole.valueOf(session.getUserRole())));
authInfo = UserAuthentication.of(
session.getUserId(),
UserRole.valueOf(session.getUserRole()));
authInfo.addAttribute("nickname",session.getNickname());
}

request.setAuthenticationInfo(authInfo);
chain.doFilter();
}
}
5 changes: 1 addition & 4 deletions src/main/java/web/filter/MemberAuthorizationFilter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package web.filter;

import http.HttpStatus;
import http.request.HttpRequest;
import http.response.HttpResponse;
import web.filter.authentication.UserRole;
Expand All @@ -13,9 +12,7 @@ public void runFilter(HttpRequest request, HttpResponse response, FilterChainCon
if(request.getAuthenticationInfo().getRole().equals(UserRole.MEMBER)){
chain.doFilter();
} else {
response.setStatus(HttpStatus.FOUND);
response.setHeader("Location", "/login");
response.setHeader("Content-Length", "0");
response.redirectTo("/login");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@

public interface AuthenticationInfo {
Optional<Long> getUserId();
Optional<Object> getAttribute(String key);
void addAttribute(String key, Object value);
UserRole getRole();
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
package web.filter.authentication;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class UnanimousAuthentication implements AuthenticationInfo {
private final UserRole USER_ROLE = UserRole.UNANIMOUS;

public UnanimousAuthentication() {
this.attributes = new HashMap<>();
}

private final Map<String, Object> attributes;

public static UnanimousAuthentication of(){
return new UnanimousAuthentication();
}

@Override
public void addAttribute(String key, Object value) {
attributes.put(key, value);
}

@Override
public Optional<Object> getAttribute(String key) {
return Optional.ofNullable(attributes.get(key));
}

@Override
public Optional<Long> getUserId() {
return Optional.empty();
}


@Override
public UserRole getRole() {
return this.USER_ROLE;
Expand Down
Loading