프로젝트가 보이지 않는 문제 수정
This commit is contained in:
parent
40c6c88e9d
commit
8f6a49cb54
|
|
@ -82,6 +82,7 @@ public class WebMvcController implements WebMvcConfigurer, ErrorController {
|
|||
if (!isLoggedIn(session)) {
|
||||
return new ModelAndView("redirect:/login");
|
||||
}
|
||||
mv.addObject("myGames", gamesMapper.getGamesByUserId(sessionUserId(session)));
|
||||
mv.setViewName("profile");
|
||||
break;
|
||||
case "signup":
|
||||
|
|
@ -114,6 +115,17 @@ public class WebMvcController implements WebMvcConfigurer, ErrorController {
|
|||
return session != null && session.getAttribute("userId") != null;
|
||||
}
|
||||
|
||||
private long sessionUserId(HttpSession session) {
|
||||
Object userId = session.getAttribute("userId");
|
||||
if (userId instanceof Number) {
|
||||
return ((Number) userId).longValue();
|
||||
}
|
||||
if (userId instanceof String) {
|
||||
return Long.parseLong((String) userId);
|
||||
}
|
||||
throw new IllegalStateException("로그인 세션에 사용자 ID가 없습니다.");
|
||||
}
|
||||
|
||||
/// 접속기기 모바일 확인 함수
|
||||
private boolean isMobileDevice(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
|
|
|
|||
|
|
@ -90,6 +90,30 @@ public interface GamesMapper {
|
|||
""")
|
||||
List<GameData> searchVisibleGames(@Param("query") String query);
|
||||
|
||||
@Select("""
|
||||
SELECT
|
||||
g.id,
|
||||
g.user_id AS userId,
|
||||
g.name,
|
||||
u.display_name AS creator,
|
||||
g.creator_note AS creatorNote,
|
||||
g.git_url AS gitUrl,
|
||||
g.webgl_path AS webglPath,
|
||||
g.thumbnail_url AS thumbnailUrl,
|
||||
g.like_count AS likeCount,
|
||||
g.is_visible AS visible,
|
||||
g.sort_order AS sortOrder,
|
||||
g.created_at AS createdAt,
|
||||
g.updated_at AS updatedAt
|
||||
FROM games g
|
||||
JOIN users u ON u.id = g.user_id
|
||||
WHERE g.user_id = #{userId}
|
||||
AND g.is_delete IS NOT TRUE
|
||||
AND u.is_delete IS NOT TRUE
|
||||
ORDER BY g.updated_at DESC, g.created_at DESC, g.id DESC
|
||||
""")
|
||||
List<GameData> getGamesByUserId(@Param("userId") long userId);
|
||||
|
||||
@Insert("""
|
||||
INSERT INTO games (
|
||||
user_id,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
|
||||
<header class="site-header">
|
||||
<div class="site-header__inner">
|
||||
<a class="logo" href="${pageContext.request.contextPath}/">비빔밥</a>
|
||||
<form class="search" method="get" action="${pageContext.request.contextPath}/">
|
||||
<input type="search" name="q" value="<c:out value='${q}'/>" placeholder="게임 이름 또는 제작자 검색" aria-label="검색"/>
|
||||
<button type="submit">검색</button>
|
||||
</form>
|
||||
<nav class="auth">
|
||||
<sec:authorize access="isAuthenticated()">
|
||||
<a href="${pageContext.request.contextPath}/games/new">게임 올리기</a>
|
||||
<form action="${pageContext.request.contextPath}/logout" method="post" class="logout-form">
|
||||
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
|
||||
<button type="submit" class="link-btn">로그아웃</button>
|
||||
</form>
|
||||
</sec:authorize>
|
||||
<sec:authorize access="!isAuthenticated()">
|
||||
<a href="${pageContext.request.contextPath}/login">로그인</a>
|
||||
<a class="btn-primary" href="${pageContext.request.contextPath}/register">회원가입</a>
|
||||
</sec:authorize>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
|
||||
<%@ page import="com.pandoli365.bibimbap.data.GameData" %>
|
||||
<%@ page import="org.springframework.web.util.HtmlUtils" %>
|
||||
<%@ page import="java.util.Collections" %>
|
||||
<%@ page import="java.util.List" %>
|
||||
<%@ page import="java.util.Locale" %>
|
||||
<!DOCTYPE html>
|
||||
<%
|
||||
|
|
@ -14,6 +17,8 @@
|
|||
String email = HtmlUtils.htmlEscape(rawEmail.isBlank() ? "이메일 정보 없음" : rawEmail);
|
||||
String avatarUrl = HtmlUtils.htmlEscape(rawAvatarUrl);
|
||||
String avatarInitial = HtmlUtils.htmlEscape(initial);
|
||||
Object rawMyGames = request.getAttribute("myGames");
|
||||
List<GameData> myGames = rawMyGames instanceof List<?> ? (List<GameData>) rawMyGames : Collections.emptyList();
|
||||
%>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
|
|
@ -224,6 +229,129 @@
|
|||
.profile-actions form {
|
||||
margin: 0;
|
||||
}
|
||||
.profile-games {
|
||||
margin-top: 1rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: var(--card-bg);
|
||||
box-shadow: 0 2px 8px var(--card-shadow);
|
||||
overflow: hidden;
|
||||
}
|
||||
.profile-games__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.profile-games__title {
|
||||
margin: 0;
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.25;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
.profile-games__count {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
.profile-games__empty {
|
||||
margin: 0;
|
||||
padding: 1.25rem 1.5rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
.profile-game-list {
|
||||
display: grid;
|
||||
}
|
||||
.profile-game {
|
||||
display: grid;
|
||||
grid-template-columns: 3.75rem minmax(0, 1fr) auto;
|
||||
gap: 0.875rem;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.profile-game:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.profile-game__thumb {
|
||||
width: 3.75rem;
|
||||
height: 3.75rem;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: rgba(232, 165, 75, 0.16);
|
||||
overflow: hidden;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.profile-game__thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.profile-game__fallback {
|
||||
width: 1.85rem;
|
||||
height: 1.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.profile-game__body {
|
||||
min-width: 0;
|
||||
}
|
||||
.profile-game__name {
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
font-size: 0.975rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.3;
|
||||
text-decoration: none;
|
||||
word-break: break-word;
|
||||
}
|
||||
.profile-game__name:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
.profile-game__meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.45rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.profile-game__status {
|
||||
border-radius: 999px;
|
||||
padding: 0.15rem 0.5rem;
|
||||
background: rgba(92, 92, 92, 0.12);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.profile-game__status--visible {
|
||||
background: rgba(232, 165, 75, 0.18);
|
||||
color: var(--text);
|
||||
}
|
||||
.profile-game__actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.profile-game__action {
|
||||
min-height: 2.25rem;
|
||||
padding: 0 0.75rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 9px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 900;
|
||||
text-decoration: none;
|
||||
}
|
||||
.profile-game__action:hover {
|
||||
border-color: rgba(232, 165, 75, 0.45);
|
||||
color: var(--accent);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.profile-main {
|
||||
padding-top: 1.5rem;
|
||||
|
|
@ -253,6 +381,21 @@
|
|||
.profile-avatar-form .profile-button {
|
||||
flex: 1;
|
||||
}
|
||||
.profile-games__head {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
.profile-game {
|
||||
grid-template-columns: 3.25rem minmax(0, 1fr);
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
.profile-game__thumb {
|
||||
width: 3.25rem;
|
||||
height: 3.25rem;
|
||||
}
|
||||
.profile-game__actions {
|
||||
grid-column: 1 / -1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
@ -289,6 +432,54 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-games">
|
||||
<div class="profile-games__head">
|
||||
<h2 class="profile-games__title">내 게임</h2>
|
||||
<span class="profile-games__count"><%= myGames.size() %>개</span>
|
||||
</div>
|
||||
<% if (myGames.isEmpty()) { %>
|
||||
<p class="profile-games__empty">등록한 게임이 없습니다.</p>
|
||||
<% } else { %>
|
||||
<div class="profile-game-list">
|
||||
<%
|
||||
for (GameData game : myGames) {
|
||||
if (game == null || game.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
String gameName = HtmlUtils.htmlEscape(game.getName() == null || game.getName().isBlank() ? "제목 없음" : game.getName());
|
||||
String rawThumbUrl = game.getThumbnailUrl();
|
||||
boolean hasImage = rawThumbUrl != null && !rawThumbUrl.isBlank();
|
||||
String thumbUrl = "";
|
||||
if (hasImage) {
|
||||
thumbUrl = rawThumbUrl.startsWith("/") ? ctx + rawThumbUrl : rawThumbUrl;
|
||||
thumbUrl = HtmlUtils.htmlEscape(thumbUrl);
|
||||
}
|
||||
boolean visible = game.getVisible() == null || game.getVisible();
|
||||
%>
|
||||
<div class="profile-game">
|
||||
<a class="profile-game__thumb" href="<%= ctx %>/game/<%= game.getId() %>" aria-hidden="true" tabindex="-1">
|
||||
<% if (hasImage) { %>
|
||||
<img src="<%= thumbUrl %>" alt="" loading="lazy" decoding="async" />
|
||||
<% } else { %>
|
||||
<img class="profile-game__fallback" src="<%= ctx %>/images/logo.png" alt="" width="40" height="40" />
|
||||
<% } %>
|
||||
</a>
|
||||
<div class="profile-game__body">
|
||||
<a class="profile-game__name" href="<%= ctx %>/game/<%= game.getId() %>"><%= gameName %></a>
|
||||
<div class="profile-game__meta">
|
||||
<span class="profile-game__status <%= visible ? "profile-game__status--visible" : "" %>"><%= visible ? "공개" : "비공개" %></span>
|
||||
<span>좋아요 <%= String.format("%,d", game.getLikeCount() == null ? 0 : game.getLikeCount()) %></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-game__actions">
|
||||
<a class="profile-game__action" href="<%= ctx %>/game/<%= game.getId() %>/edit">수정</a>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<jsp:include page="/WEB-INF/views/footer.jsp"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue