From 11de429ffa0079b291cc0db33bd487dfdfbfb629 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 11:06:38 +0900 Subject: [PATCH 01/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20post?= =?UTF-8?q?=EC=9D=98=20image,=20author=20image=20=EB=B6=88=EB=9F=AC?= =?UTF-8?q?=EC=98=A4=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?-=20router=EC=9D=98=20route=20input=EC=9D=84=20prefix=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=ED=83=90=EC=83=89?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20-=20static?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=EA=B3=BC=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EB=93=B1=EB=A1=9D=ED=95=B4=EC=84=9C=20=EB=94=B0?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=80=EC=9E=A5=ED=95=B4=EB=91=94=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=EB=A5=BC=20=EC=B2=98=EB=A6=AC=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20=EB=B6=84=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20api=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/Database.java | 4 +-- src/main/java/db/ImageManager.java | 25 ++++++++++++++++--- .../java/webserver/process/Processor.java | 15 +++++++++++ src/main/java/webserver/route/Router.java | 5 ++-- src/main/resources/static/index.html | 4 +-- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index 4687c786..aead16f6 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -121,7 +121,7 @@ public static Collection findAllUser() { public static void addPost(Post post) { try { - String imagePath = ImageManager.saveImage(post.image()); + String imagePath = ImageManager.saveImagePost(post.image()); String sql = "INSERT INTO posts(user_id, image_path, content, likes) VALUES (?, ?, ?, 0)"; @@ -154,7 +154,7 @@ public static Post getRecentPost(){ String imagePath = rs.getString("image_path"); return new Post( rs.getLong("post_id"), - ImageManager.readImage(imagePath), + ImageManager.readImagePost(imagePath), rs.getString("user_id"), rs.getString("content"), rs.getInt("likes"), diff --git a/src/main/java/db/ImageManager.java b/src/main/java/db/ImageManager.java index aa296cf3..84ce871d 100644 --- a/src/main/java/db/ImageManager.java +++ b/src/main/java/db/ImageManager.java @@ -17,12 +17,20 @@ public class ImageManager { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss_SSS"); - public static String saveImage(byte[] imageBytes) { + public static String saveImagePost(byte[] imageBytes) { + return saveImage(basePath, imageBytes); + } + + public static String saveImageProfile(byte[] imageBytes) { + return saveImage(baseProfilePath, imageBytes); + } + + private static String saveImage(String inputBasePath, byte[] imageBytes) { if (imageBytes == null || imageBytes.length == 0) { throw DBExceptionConverter.failToLoadImage(); } - File dir = new File(basePath); + File dir = new File(inputBasePath); if (!dir.exists()) { dir.mkdirs(); } @@ -42,8 +50,16 @@ public static String saveImage(byte[] imageBytes) { return fileName; } - public static byte[] readImage(String path){ - File file = new File(basePath+"/"+path); + public static byte[] readImagePost(String path){ + return readImage(basePath, path); + } + + public static byte[] readImageProfile(String path){ + return readImage(baseProfilePath, path); + } + + private static byte[] readImage(String inputBasePath, String path){ + File file = new File(inputBasePath+"/"+path); if (!file.exists() || !file.isFile()) { throw DBExceptionConverter.failToLoadImage(); } @@ -59,6 +75,7 @@ public static byte[] readImage(String path){ } catch (IOException e) { throw DBExceptionConverter.failToLoadImage(); } + return image; } } diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 5f16caf8..0bc6fdd1 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -5,6 +5,7 @@ import customException.WebException; import customException.WebStatusConverter; import db.Database; +import db.ImageManager; import model.Post; import model.User; import webserver.http.Request; @@ -98,6 +99,20 @@ public void init() { Database.addPost(post); return new Response(WebException.HTTPStatus.OK, null, Response.ContentType.PLAIN_TEXT); }); + + router.register(new Request(Request.Method.GET, "/image/profile"), request -> { + String[] pathSplit = request.path.split("/"); + byte[] image = ImageManager.readImageProfile(pathSplit[pathSplit.length-1]); + System.out.println("profile"+pathSplit[pathSplit.length-1]+" is OK."); + return new Response(WebException.HTTPStatus.OK, image, Response.contentType(request.path)); + }); + + router.register(new Request(Request.Method.GET, "/image"), request -> { + String[] pathSplit = request.path.split("/"); + byte[] image = ImageManager.readImagePost(pathSplit[pathSplit.length-1]); + System.out.println("post"+pathSplit[pathSplit.length-1]+" is OK."); + return new Response(WebException.HTTPStatus.OK, image, Response.contentType(request.path)); + }); } public Response process(Request simpleReq) { diff --git a/src/main/java/webserver/route/Router.java b/src/main/java/webserver/route/Router.java index 8b76a977..adc9bcf0 100644 --- a/src/main/java/webserver/route/Router.java +++ b/src/main/java/webserver/route/Router.java @@ -41,8 +41,9 @@ public Response route(Request req) { for (String part : parts) { if (part.isEmpty()) continue; - curNode = curNode.children.get(part); - if (curNode == null) throw WebStatusConverter.notAllowedPath(); + RouterNode tnsNode = curNode.children.get(part); + if (tnsNode == null) break; + curNode = tnsNode; } Function func = curNode.funcs.get(req.method); diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 3a04d4d7..21ba6b16 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -16,10 +16,10 @@
- +
  • From 33fc8bb4cf2c1158aeaeecd1ca7d8d14a4622a05 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 11:11:58 +0900 Subject: [PATCH 02/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20post?= =?UTF-8?q?=EC=9D=98=20=EB=B3=B8=EB=AC=B8=20=EB=B0=94=EC=9D=B8=EB=94=A9,?= =?UTF-8?q?=20header=EC=9D=98=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=82=AC?= =?UTF-8?q?=EC=A7=84=20=EB=98=90=ED=95=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 2 +- src/main/resources/static/index.html | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index 8f72cc43..ed0c351b 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -27,7 +27,7 @@ public class Config { public static final String PAGE_HEADER_LOGIN = "
    " + "
      " + "
    • " + - " " + + " " + "
    • " + "
    • " + " " + diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 21ba6b16..c1e5f37e 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -45,16 +45,6 @@

    {{post.content}} - 우리는 시스템 아키텍처에 대한 일관성 있는 접근이 필요하며, 필요한 - 모든 측면은 이미 개별적으로 인식되고 있다고 생각합니다. 즉, 응답이 - 잘 되고, 탄력적이며 유연하고 메시지 기반으로 동작하는 시스템 입니다. - 우리는 이것을 리액티브 시스템(Reactive Systems)라고 부릅니다. - 리액티브 시스템으로 구축된 시스템은 보다 유연하고, 느슨한 결합을 - 갖고, 확장성 이 있습니다. 이로 인해 개발이 더 쉬워지고 변경 사항을 - 적용하기 쉬워집니다. 이 시스템은 장애 에 대해 더 강한 내성을 지니며, - 비록 장애가 발생 하더라도, 재난이 일어나기 보다는 간결한 방식으로 - 해결합니다. 리액티브 시스템은 높은 응답성을 가지며 사용자 에게 - 효과적인 상호적 피드백을 제공합니다.

    From cb359095dab91efd3382f5239dc4bee3e609108a Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 11:17:40 +0900 Subject: [PATCH 03/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=8B=9C=20name=EC=9D=98=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserExceptionConverter.java | 16 ++++++++++- src/main/java/db/Database.java | 27 ++++++++++++++++++- .../java/webserver/process/UserProcessor.java | 3 ++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/main/java/customException/UserExceptionConverter.java b/src/main/java/customException/UserExceptionConverter.java index 2a74197a..39b091a0 100644 --- a/src/main/java/customException/UserExceptionConverter.java +++ b/src/main/java/customException/UserExceptionConverter.java @@ -1,12 +1,18 @@ package customException; public class UserExceptionConverter { - public static WebException conflictUser() { + public static WebException conflictUserID() { return new WebException( WebException.HTTPStatus.CONFLICT, "해당 아이디를 사용하는 회원이 존재합니다." ); } + public static WebException conflictUserName() { + return new WebException( + WebException.HTTPStatus.CONFLICT, + "해당 이름을 사용하는 회원이 존재합니다." + ); + } public static WebException needUserId() { return new WebException( @@ -15,6 +21,14 @@ public static WebException needUserId() { ); } + + public static WebException needUserName() { + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "사용자 이름을 요청에 포함해야 합니다." + ); + } + public static WebException needUserData() { return new WebException( WebException.HTTPStatus.BAD_REQUEST, diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index aead16f6..3ba8e402 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -25,7 +25,7 @@ public static void init(){ stmt.execute("CREATE TABLE users (\n" + " user_id VARCHAR(50) PRIMARY KEY,\n" + " password VARCHAR(255) NOT NULL,\n" + - " name VARCHAR(50) NOT NULL,\n" + + " name VARCHAR(50) NOT NULL UNIQUE ,\n" + " email VARCHAR(100) NOT NULL\n" + ");\n"); @@ -96,6 +96,31 @@ public static User findUserById(String userId) { } } + public static User findUserByName(String name) { + try { + if (name == null || name.isBlank()) throw UserExceptionConverter.needUserName(); + String sql = "SELECT * FROM users WHERE name = (?)"; + PreparedStatement pstmt = conn.prepareStatement(sql); + pstmt.setString(1, name); + + ResultSet result = pstmt.executeQuery(); + if (!result.next()) { + return null; + } + + return new User( + result.getString("user_id"), + result.getString("password"), + result.getString("name"), + result.getString("email") + ); + } + catch (SQLException e){ + logger.error(e.getMessage()); + throw DBExceptionConverter.failToAddUser(); + } + } + public static Collection findAllUser() { try { Collection users = new ArrayList<>(); diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index 9074597a..887a96d7 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -26,7 +26,8 @@ public byte[] createUser(Request request) { .map(RequestBody::getContentString) .orElseThrow(UserExceptionConverter::needUserData)); - if (Database.findUserById(user.getUserId()) != null) throw UserExceptionConverter.conflictUser(); + if (Database.findUserById(user.getUserId()) != null) throw UserExceptionConverter.conflictUserID(); + if (Database.findUserByName(user.getName()) != null) throw UserExceptionConverter.conflictUserName(); Database.addUser(user); return user.toString().getBytes(); From 46da4d51a53e917cdac1e57253c7c4724a67ebee Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 11:23:24 +0900 Subject: [PATCH 04/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/Database.java | 15 +++++---------- src/main/java/model/User.java | 14 ++++---------- src/main/java/webserver/RequestHandler.java | 2 +- .../java/webserver/process/UserProcessor.java | 3 --- src/main/resources/static/registration/index.html | 11 ----------- src/main/resources/static/script/alert.js | 2 -- 6 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index 3ba8e402..cea44a00 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -25,8 +25,7 @@ public static void init(){ stmt.execute("CREATE TABLE users (\n" + " user_id VARCHAR(50) PRIMARY KEY,\n" + " password VARCHAR(255) NOT NULL,\n" + - " name VARCHAR(50) NOT NULL UNIQUE ,\n" + - " email VARCHAR(100) NOT NULL\n" + + " name VARCHAR(50) NOT NULL UNIQUE\n" + ");\n"); stmt.execute("CREATE TABLE posts (\n" + @@ -55,13 +54,12 @@ public static void init(){ public static void addUser(User user) { try { - String sql = "INSERT INTO users(user_id, password, name, email) VALUES (?, ?, ?, ?)"; + String sql = "INSERT INTO users(user_id, password, name) VALUES (?, ?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, user.getUserId()); pstmt.setString(2, user.getPassword()); pstmt.setString(3, user.getName()); - pstmt.setString(4, user.getEmail()); pstmt.executeUpdate(); } @@ -86,8 +84,7 @@ public static User findUserById(String userId) { return new User( result.getString("user_id"), result.getString("password"), - result.getString("name"), - result.getString("email") + result.getString("name") ); } catch (SQLException e){ @@ -111,8 +108,7 @@ public static User findUserByName(String name) { return new User( result.getString("user_id"), result.getString("password"), - result.getString("name"), - result.getString("email") + result.getString("name") ); } catch (SQLException e){ @@ -131,8 +127,7 @@ public static Collection findAllUser() { while(result.next()){ users.add(new User(result.getString("user_id"), result.getString("password"), - result.getString("name"), - result.getString("email"))); + result.getString("name"))); } return users; } diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index ea04a0c2..ce2a689d 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -7,18 +7,16 @@ public class User { private String userId; private String password; private String name; - private String email; private byte[] image; private String imagePath; - public User(String userId, String password, String name, String email) { - if (userId == null || password == null || name == null || email == null - || userId.isBlank() || password.isBlank() || name.isBlank() || email.isBlank()) + public User(String userId, String password, String name) { + if (userId == null || password == null || name == null + || userId.isBlank() || password.isBlank() || name.isBlank()) throw UserExceptionConverter.needUserData(); this.userId = userId; this.password = password; this.name = name; - this.email = email; this.image = null; this.imagePath = Config.IMAGE_DEFAULT_PROFILE_NAME; } @@ -35,10 +33,6 @@ public String getName() { return name; } - public String getEmail() { - return email; - } - public String getImagePath() { return this.imagePath; } @@ -49,6 +43,6 @@ public void setImagePath(String path) { @Override public String toString() { - return "User [userId=" + userId + ", password=" + password + ", name=" + name + ", email=" + email + "]"; + return "User [userId=" + userId + ", password=" + password + ", name=" + name + "]"; } } diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index edf4ba1d..2321787e 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -25,7 +25,7 @@ public RequestHandler(Socket connectionSocket) { } public static void addTestUser(){ - Database.addUser(new User("tttt", "1234", "현지", "t@n.c")); + Database.addUser(new User("tttt", "1234", "현지")); } public void run() { diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index 887a96d7..00b5effc 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -20,9 +20,6 @@ public byte[] createUser(Request request) { .map(RequestBody::getContentString) .orElseThrow(UserExceptionConverter::needUserData), Optional.ofNullable(request.bodyParam.get("name")) - .map(RequestBody::getContentString) - .orElseThrow(UserExceptionConverter::needUserData), - Optional.ofNullable(request.bodyParam.get("email")) .map(RequestBody::getContentString) .orElseThrow(UserExceptionConverter::needUserData)); diff --git a/src/main/resources/static/registration/index.html b/src/main/resources/static/registration/index.html index 73452adf..d03f3d1c 100644 --- a/src/main/resources/static/registration/index.html +++ b/src/main/resources/static/registration/index.html @@ -34,17 +34,6 @@

    회원가입

    id="userId" />
-
-

이메일

- -

닉네임

e.preventDefault(); // 기본 submit 막기 const userId = document.querySelector("#userId").value; const password = document.querySelector("#password").value; - const email = document.querySelector("#email").value; const name = document.querySelector("#name").value; const response = await fetch("/user/create", { @@ -64,7 +63,6 @@ document.getElementById("registration")?.addEventListener("submit", async (e) => body: new URLSearchParams({ userId, password, - email, name }) }); From ccb894523bd53e5368a8f7e092d2f1f1a603c0ad Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 11:50:21 +0900 Subject: [PATCH 05/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20main=20?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B0=80=EB=8A=94=20=EB=A7=81=ED=81=AC?= =?UTF-8?q?=EB=A5=BC=20=EB=AA=A8=EB=91=90=20/=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=80=EB=8F=84=EB=A1=9D=20=EB=B3=91=ED=95=A9.=20css=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/article/index.html | 3 +- src/main/resources/static/comment/index.html | 2 +- src/main/resources/static/main/index.html | 265 +++++++++---------- 3 files changed, 131 insertions(+), 139 deletions(-) diff --git a/src/main/resources/static/article/index.html b/src/main/resources/static/article/index.html index cff452d1..4f462812 100644 --- a/src/main/resources/static/article/index.html +++ b/src/main/resources/static/article/index.html @@ -5,11 +5,12 @@ +
- + {{page.header}}
diff --git a/src/main/resources/static/comment/index.html b/src/main/resources/static/comment/index.html index 35df2b25..fa482681 100644 --- a/src/main/resources/static/comment/index.html +++ b/src/main/resources/static/comment/index.html @@ -9,7 +9,7 @@
- +
  • 글쓰기 diff --git a/src/main/resources/static/main/index.html b/src/main/resources/static/main/index.html index b5b91be0..681d4700 100644 --- a/src/main/resources/static/main/index.html +++ b/src/main/resources/static/main/index.html @@ -1,149 +1,140 @@ - - - - - - - - -
    -
    - - -
    -
    -
    - - -
    -
      -
    • - -
    • -
    • - -
    • -
    + + + + + + + + +
    +
    + + {{page.header}} +
    +
    +
    + + +
    +
      +
    • -
    -

    - 우리는 시스템 아키텍처에 대한 일관성 있는 접근이 필요하며, 필요한 - 모든 측면은 이미 개별적으로 인식되고 있다고 생각합니다. 즉, 응답이 - 잘 되고, 탄력적이며 유연하고 메시지 기반으로 동작하는 시스템 입니다. - 우리는 이것을 리액티브 시스템(Reactive Systems)라고 부릅니다. - 리액티브 시스템으로 구축된 시스템은 보다 유연하고, 느슨한 결합을 - 갖고, 확장성 이 있습니다. 이로 인해 개발이 더 쉬워지고 변경 사항을 - 적용하기 쉬워집니다. 이 시스템은 장애 에 대해 더 강한 내성을 지니며, - 비록 장애가 발생 하더라도, 재난이 일어나기 보다는 간결한 방식으로 - 해결합니다. 리액티브 시스템은 높은 응답성을 가지며 사용자 에게 - 효과적인 상호적 피드백을 제공합니다. -

    -
    -
      -
    • -
      - -

      account

      -
      -

      - 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 - 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 - 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 - 재판을 받지 아니한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 - 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 - 조직·직무범위 기타 필요한 사항은 법률로 정한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 - 정한다. -

    • -
    • +
    • -
    • +
    • -
    • + {{post.commentNum}}
    • -
    - +
    +

    + {{post.content}} +

    - +
      +
    • +
      + +

      account

      +
      +

      + 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 + 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 + 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 + 재판을 받지 아니한다. +

      +
    • +
    • +
      + +

      account

      +
      +

      + 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 + 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 + 조직·직무범위 기타 필요한 사항은 법률로 정한다. +

      +
    • +
    • +
      + +

      account

      +
      +

      + 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 + 정한다. +

      +
    • + + + + +
    + +
    + 글쓰기 +
    + + + From 3715fb41095ed5f6ed54d70f4d775aa3c34b3e5a Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 14:05:16 +0900 Subject: [PATCH 06/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EA=B8=B0=EB=8A=A5=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customException/DBExceptionConverter.java | 7 ++++ .../PostExceptionConverter.java | 7 ++++ src/main/java/db/Database.java | 37 +++++++++++++++++++ src/main/java/webserver/http/Response.java | 1 + .../java/webserver/process/Processor.java | 18 +++++++++ src/main/resources/static/index.html | 9 +++-- .../resources/static/script/extraEvent.js | 21 +++++++++++ 7 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/static/script/extraEvent.js diff --git a/src/main/java/customException/DBExceptionConverter.java b/src/main/java/customException/DBExceptionConverter.java index 0c136063..7d37c880 100644 --- a/src/main/java/customException/DBExceptionConverter.java +++ b/src/main/java/customException/DBExceptionConverter.java @@ -22,6 +22,13 @@ public static WebException failToFindUser() { ); } + public static WebException failToUpdatePost(){ + return new WebException( + WebException.HTTPStatus.INTERNAL_SERVER_ERROR, + "포스트의 좋아요를 업데이트 하는 것에 실패했습니다." + ); + } + public static WebException failToAddPost() { return new WebException( diff --git a/src/main/java/customException/PostExceptionConverter.java b/src/main/java/customException/PostExceptionConverter.java index e5b02ba4..f470d50a 100644 --- a/src/main/java/customException/PostExceptionConverter.java +++ b/src/main/java/customException/PostExceptionConverter.java @@ -14,4 +14,11 @@ public static WebException badContentPost() { "포스트의 내용이 존재하지 않습니다." ); } + + public static WebException badPostId() { + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "포스트 id가 형식에 맞지 않습니다." + ); + } } diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index cea44a00..53670c70 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -224,4 +224,41 @@ public static Collection findCommentsByPost(long postId) { throw DBExceptionConverter.failToAddUser(); } } + + public static int updatePostLikes(long postId) { + String sql = """ + UPDATE posts + SET likes = likes + 1 + WHERE post_id = ? + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setLong(1, postId); + + int updated = pstmt.executeUpdate(); + if (updated == 0) { + throw PostExceptionConverter.notFoundPost(); + } + + return getPostLikes(postId); + } + catch (SQLException e){ + throw DBExceptionConverter.failToUpdatePost(); + } + } + + + public static int getPostLikes(long postId) { + String sql = "SELECT likes FROM posts WHERE post_id = ?"; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setLong(1, postId); + ResultSet rs = pstmt.executeQuery(); + rs.next(); + return rs.getInt("likes"); + } + catch (SQLException e){ + throw PostExceptionConverter.notFoundPost(); + } + } } diff --git a/src/main/java/webserver/http/Response.java b/src/main/java/webserver/http/Response.java index 4ac434ae..5afd767f 100644 --- a/src/main/java/webserver/http/Response.java +++ b/src/main/java/webserver/http/Response.java @@ -13,6 +13,7 @@ public enum ContentType { HTML("text/html;charset=utf-8"), CSS("text/css"), JS("application/javascript"), + JSON("application/json"), PNG("image/png"), JPG("image/png"), JPEG("image/jpeg"), diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 0bc6fdd1..d8c94f8a 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -17,6 +17,7 @@ import webserver.parse.PageStruct; import webserver.route.Router; +import java.io.IOException; import java.nio.charset.StandardCharsets; public class Processor { @@ -113,6 +114,23 @@ public void init() { System.out.println("post"+pathSplit[pathSplit.length-1]+" is OK."); return new Response(WebException.HTTPStatus.OK, image, Response.contentType(request.path)); }); + + + router.register(new Request(Request.Method.POST, "/post/like"), request -> { + String[] pathSplit = request.path.split("/"); + try{ + long postId = Long.parseLong(pathSplit[pathSplit.length-1]); + int likes = Database.updatePostLikes(postId); + return new Response( + WebException.HTTPStatus.OK, + ("{\"likes\":" + likes + "}").getBytes(), + Response.ContentType.JSON + ); + } + catch (NumberFormatException e){ + throw PostExceptionConverter.badPostId(); + } + }); } public Response process(Request simpleReq) { diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index c1e5f37e..0ff706fa 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -23,15 +23,17 @@
    + diff --git a/src/main/resources/static/script/extraEvent.js b/src/main/resources/static/script/extraEvent.js new file mode 100644 index 00000000..405c3311 --- /dev/null +++ b/src/main/resources/static/script/extraEvent.js @@ -0,0 +1,21 @@ +function addListenerLikeAction(){ + const btn = document.getElementById('post__like__btn'); + + btn.addEventListener('click', async () => { + const postId = btn.dataset.postId; + if (!postId) return; + + const response = await fetch(`/post/like/${postId}`, { + method: "POST" + }); + + if (!response.ok) return; + + const data = await response.json(); + + document.querySelector('.post__like__count').textContent = data.likes; + }); + +} + +addListenerLikeAction(); \ No newline at end of file From b7dd071d5a1e6ed9a1fb3a33f33a48a048f28a08 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 14:18:13 +0900 Subject: [PATCH 07/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=EC=97=86=EB=8A=94=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=85=8B=20=EC=97=B0=EB=8F=99=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 0ff706fa..2d4c26d8 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -28,7 +28,7 @@
  • -
  • From 5f4a8266bf1d69549b211afa610adf6a1d2a47a0 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 15:53:59 +0900 Subject: [PATCH 08/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=9E=91=EC=84=B1=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 4 + .../CommentExceptionConverter.java | 20 +++++ src/main/java/db/Database.java | 37 ++++++++- src/main/java/model/Comment.java | 2 +- .../webserver/process/CommentProcessor.java | 24 ++++++ .../java/webserver/process/Processor.java | 75 ++++++++++++++----- .../process/StaticFileProcessor.java | 2 +- src/main/java/webserver/route/Router.java | 9 ++- src/main/resources/static/comment/index.html | 15 +--- src/main/resources/static/index.html | 2 +- src/main/resources/static/script/alert.js | 15 ++++ .../resources/static/script/extraEvent.js | 1 + 12 files changed, 167 insertions(+), 39 deletions(-) create mode 100644 src/main/java/customException/CommentExceptionConverter.java create mode 100644 src/main/java/webserver/process/CommentProcessor.java diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index ed0c351b..c222e53f 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -12,6 +12,8 @@ public class Config { public static final String COMMENT_PAGE_PATH = "/comment/index.html"; public static final String ARTICLE_PAGE_PATH = "/article/index.html"; public static final String NOPOST_PAGE_PATH = "/exception/nopost.html"; + public static final String COMMENT_PAGE_PATH_PREFIX = "/comment"; + public static final String POST_PAGE_PATH = "/post"; public static final String HEADER_CONTENT_LENGTH = "content-length"; public static final String HEADER_LOCATION = "location"; @@ -24,6 +26,8 @@ public class Config { public static final String IMAGE_PROFILE_BASE_PATH = "./src/main/resources/uploads/profiles"; public static final String IMAGE_DEFAULT_PROFILE_NAME = "default.png"; + public static final String POST_ID_QUERY_NAME = "postId"; + public static final String PAGE_HEADER_LOGIN = "
    " + "
      " + "
    • " + diff --git a/src/main/java/customException/CommentExceptionConverter.java b/src/main/java/customException/CommentExceptionConverter.java new file mode 100644 index 00000000..900a5894 --- /dev/null +++ b/src/main/java/customException/CommentExceptionConverter.java @@ -0,0 +1,20 @@ +package customException; + +import common.Config; + +public class CommentExceptionConverter { + public static WebException noPostId() { + return new WebException( + WebException.HTTPStatus.MOVED_TEMPORALLY, + "댓글 작성 페이지에 대한 잘못된 접근입니다.", + Config.DEFAULT_PAGE_PATH + ); + } + + public static WebException badContentComment(){ + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "댓글 내용이 비어있습니다." + ); + } +} diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index 53670c70..04dc36e5 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -113,7 +113,7 @@ public static User findUserByName(String name) { } catch (SQLException e){ logger.error(e.getMessage()); - throw DBExceptionConverter.failToAddUser(); + throw DBExceptionConverter.failToFindUser(); } } @@ -158,6 +158,36 @@ public static void addPost(Post post) { } } + public static Post getPostByPostId(long postId){ + String sql = """ + SELECT post_id, user_id, image_path, content, likes + FROM posts + WHERE post_id = ? + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setLong(1, postId); + ResultSet rs = pstmt.executeQuery(); + + if (!rs.next()) { + return null; + } + String imagePath = rs.getString("image_path"); + return new Post( + rs.getLong("post_id"), + ImageManager.readImagePost(imagePath), + rs.getString("user_id"), + rs.getString("content"), + rs.getInt("likes"), + imagePath + ); + + } catch (SQLException e) { + logger.error(e.getMessage()); + throw DBExceptionConverter.failToFindPost(); + } + } + public static Post getRecentPost(){ String sql = """ SELECT post_id, user_id, image_path, content, likes @@ -207,7 +237,7 @@ public static void addComment(Comment comment) { public static Collection findCommentsByPost(long postId) { try { Collection comments = new ArrayList<>(); - String sql = "SELECT * FROM comment WHERE post_id = (?)"; + String sql = "SELECT * FROM comments WHERE post_id = (?)"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setLong(1, postId); @@ -221,7 +251,8 @@ public static Collection findCommentsByPost(long postId) { return comments; } catch (SQLException e){ - throw DBExceptionConverter.failToAddUser(); + logger.error(e.getMessage()); + throw DBExceptionConverter.failToFindComment(); } } diff --git a/src/main/java/model/Comment.java b/src/main/java/model/Comment.java index 9d502954..316c710d 100644 --- a/src/main/java/model/Comment.java +++ b/src/main/java/model/Comment.java @@ -1,7 +1,7 @@ package model; public record Comment(long commentId, long postId, String authorId, String content) { - Comment(long postId, String userId, String content) { + public Comment(long postId, String userId, String content) { this(0, postId, userId, content); } } diff --git a/src/main/java/webserver/process/CommentProcessor.java b/src/main/java/webserver/process/CommentProcessor.java new file mode 100644 index 00000000..e9f25c06 --- /dev/null +++ b/src/main/java/webserver/process/CommentProcessor.java @@ -0,0 +1,24 @@ +package webserver.process; + +import customException.UserExceptionConverter; +import db.Database; +import model.Comment; +import model.User; +import webserver.http.Request; +import webserver.http.RequestBody; + +import java.util.Optional; + +public class CommentProcessor { +// public Comment createComment(Request request, String userId, long postId) { +// +// Comment comment = new Comment(postId, userId, request.) +// +// if (Database.findUserById(user.getUserId()) != null) throw UserExceptionConverter.conflictUserID(); +// if (Database.findUserByName(user.getName()) != null) throw UserExceptionConverter.conflictUserName(); +// Database.addUser(user); +// +// return user.toString().getBytes(); +// } + +} diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index d8c94f8a..77e958ac 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -1,11 +1,13 @@ package webserver.process; import common.Config; +import customException.CommentExceptionConverter; import customException.PostExceptionConverter; import customException.WebException; import customException.WebStatusConverter; import db.Database; import db.ImageManager; +import model.Comment; import model.Post; import model.User; import webserver.http.Request; @@ -44,7 +46,7 @@ public void init() { router.register(new Request(Request.Method.GET, "/login"), (request) -> { request.path = Config.LOGIN_PAGE_PATH; - if(userProcessor.getUser(request) != null){ + if (userProcessor.getUser(request) != null) { request.path = Config.DEFAULT_PAGE_PATH; } return process(request); @@ -91,7 +93,7 @@ public void init() { router.register(new Request(Request.Method.POST, "/post/create"), request -> { User user = userProcessor.getUserOrException(request); - if(request.bodyParam.getOrDefault("content", null)==null){ + if (request.bodyParam.getOrDefault("content", null) == null) { throw PostExceptionConverter.badContentPost(); } Post post = new Post(request.bodyParam.getOrDefault("image", new RequestBody("")).getContent(), @@ -103,34 +105,65 @@ public void init() { router.register(new Request(Request.Method.GET, "/image/profile"), request -> { String[] pathSplit = request.path.split("/"); - byte[] image = ImageManager.readImageProfile(pathSplit[pathSplit.length-1]); - System.out.println("profile"+pathSplit[pathSplit.length-1]+" is OK."); + byte[] image = ImageManager.readImageProfile(pathSplit[pathSplit.length - 1]); + System.out.println("profile" + pathSplit[pathSplit.length - 1] + " is OK."); return new Response(WebException.HTTPStatus.OK, image, Response.contentType(request.path)); }); router.register(new Request(Request.Method.GET, "/image"), request -> { String[] pathSplit = request.path.split("/"); - byte[] image = ImageManager.readImagePost(pathSplit[pathSplit.length-1]); - System.out.println("post"+pathSplit[pathSplit.length-1]+" is OK."); + byte[] image = ImageManager.readImagePost(pathSplit[pathSplit.length - 1]); + System.out.println("post" + pathSplit[pathSplit.length - 1] + " is OK."); return new Response(WebException.HTTPStatus.OK, image, Response.contentType(request.path)); }); router.register(new Request(Request.Method.POST, "/post/like"), request -> { String[] pathSplit = request.path.split("/"); - try{ - long postId = Long.parseLong(pathSplit[pathSplit.length-1]); + try { + long postId = Long.parseLong(pathSplit[pathSplit.length - 1]); int likes = Database.updatePostLikes(postId); return new Response( WebException.HTTPStatus.OK, ("{\"likes\":" + likes + "}").getBytes(), Response.ContentType.JSON ); - } - catch (NumberFormatException e){ + } catch (NumberFormatException e) { throw PostExceptionConverter.badPostId(); } }); + + router.register(new Request(Request.Method.GET, "/comment"), request -> { + String[] pathSplit = request.path.split("/"); + try { + request.path = Config.COMMENT_PAGE_PATH; + request.queryParam.put(Config.POST_ID_QUERY_NAME, pathSplit[pathSplit.length - 1]); + return process(request); + } catch (NumberFormatException e) { + throw CommentExceptionConverter.noPostId(); + } + }); + + router.register(new Request(Request.Method.POST, "/comment"), request -> { + String[] pathSplit = request.path.split("/"); + User user = userProcessor.getUserOrException(request); + if (request.bodyParam.getOrDefault("content", null) == null) { + throw CommentExceptionConverter.badContentComment(); + } + try { + long postId = Long.parseLong(pathSplit[pathSplit.length - 1]); + Database.addComment(new Comment(postId, user.getUserId(), request.bodyParam.get("content").toString())); + Response response = new Response( + WebException.HTTPStatus.MOVED_PERMANENTLY, + null, + Response.ContentType.PLAIN_TEXT + ); + response.addHeader(Config.HEADER_LOCATION, Config.POST_PAGE_PATH + "/" + postId); + return response; + } catch (NumberFormatException e) { + throw CommentExceptionConverter.noPostId(); + } + }); } public Response process(Request simpleReq) { @@ -147,7 +180,7 @@ public Response process(Request simpleReq) { template = userReplacer.replace(user, template); PostViewer postViewer = getPostViewer(simpleReq); body = postReplacer.replace(postViewer, template).getBytes(); - if(postViewer == null && Router.needPostData(simpleReq.path)){ + if (postViewer == null && Router.needPostData(simpleReq.path)) { body = getNoPostExceptionPage(simpleReq, user); } @@ -160,20 +193,26 @@ public Response process(Request simpleReq) { return response; } - private byte[] getNoPostExceptionPage(Request request, User user){ + private byte[] getNoPostExceptionPage(Request request, User user) { request.path = Config.NOPOST_PAGE_PATH; byte[] body = StaticFileProcessor.processReq(request); - if(body == null) throw WebStatusConverter.cannotFindNoPostPage(); + if (body == null) throw WebStatusConverter.cannotFindNoPostPage(); String template = pageReplacer.replace(pageStruct, new String(body)); return userReplacer.replace(user, template).getBytes(StandardCharsets.UTF_8); } - private PostViewer getPostViewer(Request request){ - Post post = Database.getRecentPost(); - if(post == null) return null; + private PostViewer getPostViewer(Request request) { + Post post = null; + if (request.queryParam.get(Config.POST_ID_QUERY_NAME) != null) { + long postId = Long.parseLong(request.queryParam.get(Config.POST_ID_QUERY_NAME)); + post = Database.getPostByPostId(postId); + } else { + post = Database.getRecentPost(); + } + if (post == null) return null; User author = Database.findUserById(post.userId()); - if(author == null) return null; - return new PostViewer(post, author, 0); + if (author == null) return null; + return new PostViewer(post, author, Database.findCommentsByPost(post.postId()).size()); } } diff --git a/src/main/java/webserver/process/StaticFileProcessor.java b/src/main/java/webserver/process/StaticFileProcessor.java index 5a0f93a2..04a0c5cf 100644 --- a/src/main/java/webserver/process/StaticFileProcessor.java +++ b/src/main/java/webserver/process/StaticFileProcessor.java @@ -12,7 +12,7 @@ public class StaticFileProcessor { public static byte[] processReq(Request simpleReq) { byte[] result = "".getBytes(); - if (simpleReq != null && simpleReq.method== Request.Method.GET) { + if (simpleReq != null && simpleReq.method == Request.Method.GET) { result = getStaticSources(simpleReq.path); } return result; diff --git a/src/main/java/webserver/route/Router.java b/src/main/java/webserver/route/Router.java index adc9bcf0..af19114d 100644 --- a/src/main/java/webserver/route/Router.java +++ b/src/main/java/webserver/route/Router.java @@ -12,14 +12,15 @@ public class Router { private final RouterNode root = new RouterNode(); - public static boolean needLogin(String path){ + public static boolean needLogin(String path) { return (path.compareTo(Config.MY_PAGE_PAGE_PATH) == 0) || (path.compareTo("/mypage") == 0) - || (path.compareTo("/write") ==0)|| (path.compareTo(Config.ARTICLE_PAGE_PATH)==0); + || (path.compareTo("/write") == 0) || (path.compareTo(Config.ARTICLE_PAGE_PATH) == 0) + || (path.startsWith(Config.COMMENT_PAGE_PATH_PREFIX)); } - public static boolean needPostData(String path){ + public static boolean needPostData(String path) { return (path.compareTo(Config.DEFAULT_PAGE_PATH) == 0) || (path.compareTo("/") == 0) - || (path.compareTo(Config.MAIN_PAGE_PATH)==0); + || (path.compareTo(Config.MAIN_PAGE_PATH) == 0); } public void register(Request req, Function func) { diff --git a/src/main/resources/static/comment/index.html b/src/main/resources/static/comment/index.html index fa482681..a4011473 100644 --- a/src/main/resources/static/comment/index.html +++ b/src/main/resources/static/comment/index.html @@ -10,16 +10,7 @@
      - + {{page.header}}

      댓글 작성

      @@ -30,13 +21,15 @@

      댓글 작성

      class="input_textfield" placeholder="글의 내용을 입력하세요" autocomplete="username" + name="content" >
      diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 2d4c26d8..e5c80da1 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -120,7 +120,7 @@
    + + diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index 253f006a..847432f6 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -33,13 +33,22 @@ async function alertCall(response){ function addListenerAddComment(){ const btn = document.getElementById('create_comment_btn'); - btn.addEventListener('click', async () => { + btn.addEventListener('submit', async () => { const postId = btn.dataset.postId; if (!postId) return; + const content = document.querySelector("#content").value; const response = await fetch(`/comment/${postId}`, { - method: "POST" + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + content + }) }); + + await formResponseProcessToMain(response); }); } From f9e2d31fe7e22f3a48c960170692a706acd4747f Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 16:35:44 +0900 Subject: [PATCH 10/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=9E=91=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/webserver/process/Processor.java | 4 ++-- src/main/resources/static/comment/index.html | 8 +++++--- src/main/resources/static/script/alert.js | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 77e958ac..99aecf65 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -154,11 +154,11 @@ public void init() { long postId = Long.parseLong(pathSplit[pathSplit.length - 1]); Database.addComment(new Comment(postId, user.getUserId(), request.bodyParam.get("content").toString())); Response response = new Response( - WebException.HTTPStatus.MOVED_PERMANENTLY, + WebException.HTTPStatus.CREATED, null, Response.ContentType.PLAIN_TEXT ); - response.addHeader(Config.HEADER_LOCATION, Config.POST_PAGE_PATH + "/" + postId); + //response.addHeader(Config.HEADER_LOCATION, Config.POST_PAGE_PATH + "/" + postId); return response; } catch (NumberFormatException e) { throw CommentExceptionConverter.noPostId(); diff --git a/src/main/resources/static/comment/index.html b/src/main/resources/static/comment/index.html index e58ccb33..b351b5e8 100644 --- a/src/main/resources/static/comment/index.html +++ b/src/main/resources/static/comment/index.html @@ -15,7 +15,8 @@

댓글 작성

-
+

내용

- -
  • + + +
    + + -
  • -
  • - {{post.commentNum}} -
  • - - -
    -

    - {{post.content}} -

    -
    -
      -
    • -
      - -

      account

      -

      - 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 - 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 - 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 - 재판을 받지 아니한다. +

      + {{post.content}}

      -
    • -
    • -
      - -

      account

      -
      -

      - 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 - 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 - 조직·직무범위 기타 필요한 사항은 법률로 정한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 - 정한다. -

      -
    • - - - - -
    -
    +
      +
    • +
      + +

      account

      +
      +

      + 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 + 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 + 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 + 재판을 받지 아니한다. +

      +
    • +
    • +
      + +

      account

      +
      +

      + 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 + 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 + 조직·직무범위 기타 필요한 사항은 법률로 정한다. +

    • -
    • +
      + +

      account

      +
      +

      + 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 + 정한다. +

    • - -
    + + + + + -
    - 글쓰기
    + 글쓰기 +
    - - - + + + diff --git a/src/main/resources/static/login/index.html b/src/main/resources/static/login/index.html index d84d89f5..afc77724 100644 --- a/src/main/resources/static/login/index.html +++ b/src/main/resources/static/login/index.html @@ -3,13 +3,14 @@ - - + + +
    - +
    - + diff --git a/src/main/resources/static/main/index.html b/src/main/resources/static/main/index.html index 681d4700..a71c6c0b 100644 --- a/src/main/resources/static/main/index.html +++ b/src/main/resources/static/main/index.html @@ -1,140 +1,144 @@ - - - - - + + + + + +
    -
    - - {{page.header}} -
    -
    -
    - - -
    -
      -
    • - -
    • -
    • - -
    • -
    • - +
    • +
    • + +
    • +
    • + +
    • +
    • + {{post.commentNum}} +
    • +
    + +
    +

    + {{post.content}} +

    +
    +
      +
    • +
      + +

      account

      +
      +

      + 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 + 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 + 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 + 재판을 받지 아니한다. +

      +
    • +
    • +
      + +

      account

      +
      +

      + 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 + 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 + 조직·직무범위 기타 필요한 사항은 법률로 정한다. +

      +
    • +
    • +
      + +

      account

      +
      +

      + 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 + 정한다. +

      +
    • + + + + - -
    • - {{post.commentNum}} -
    - -
    -

    - {{post.content}} -

    +
    -
      -
    • -
      - -

      account

      -
      -

      - 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 - 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 - 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 - 재판을 받지 아니한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 - 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 - 조직·직무범위 기타 필요한 사항은 법률로 정한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 - 정한다. -

      -
    • - - - - -
    - - - 글쓰기 + 글쓰기 - + + diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index 46db19b3..d3546e32 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -3,13 +3,14 @@ - - + + +
    - +
    • 글쓰기 @@ -77,6 +78,6 @@

      마이페이지

    - + diff --git a/src/main/resources/static/registration/index.html b/src/main/resources/static/registration/index.html index d03f3d1c..6d182424 100644 --- a/src/main/resources/static/registration/index.html +++ b/src/main/resources/static/registration/index.html @@ -3,13 +3,14 @@ - - + + +
    - +
    - + From 07f4dfdab1918b8a09cb392ea2685da55fe703d6 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 17:15:25 +0900 Subject: [PATCH 14/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=ED=83=90?= =?UTF-8?q?=EC=83=89=EC=B0=BD=EC=9D=84=20=ED=86=B5=ED=95=9C=20post=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=20=EC=8B=9C=20=ED=95=B4=EB=8B=B9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20post=EA=B0=80=20=EC=97=86=EB=8B=A4=EB=A9=B4=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/webserver/process/Processor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 0ba74788..db84024a 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -140,7 +140,9 @@ public void init() { String[] pathSplit = request.path.split("/"); try { request.path = Config.MAIN_PAGE_PATH; - request.queryParam.put(Config.POST_ID_QUERY_NAME, pathSplit[pathSplit.length - 1]); + if(Database.getPostByPostId(Long.parseLong(pathSplit[pathSplit.length - 1])) != null) + request.queryParam.put(Config.POST_ID_QUERY_NAME, pathSplit[pathSplit.length - 1]); + return process(request); } catch (NumberFormatException e) { From 8da6a56880b4cf869283cf10174b494b622e244e Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 17:22:06 +0900 Subject: [PATCH 15/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=83=9D=EC=84=B1=20=EC=8B=9C,=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=ED=8F=AC=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/webserver/process/CommentProcessor.java | 4 +--- src/main/resources/static/script/alert.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/webserver/process/CommentProcessor.java b/src/main/java/webserver/process/CommentProcessor.java index a721f8f9..dd5c7719 100644 --- a/src/main/java/webserver/process/CommentProcessor.java +++ b/src/main/java/webserver/process/CommentProcessor.java @@ -22,13 +22,11 @@ public Response createComment(Request request, User user) { try { long postId = Long.parseLong(pathSplit[pathSplit.length - 1]); Database.addComment(new Comment(postId, user.getUserId(), request.bodyParam.get("content").toString())); - Response response = new Response( + return new Response( WebException.HTTPStatus.CREATED, null, Response.ContentType.PLAIN_TEXT ); - //response.addHeader(Config.HEADER_LOCATION, Config.POST_PAGE_PATH + "/" + postId); - return response; } catch (NumberFormatException e) { throw CommentExceptionConverter.noPostId(); } diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index 9d874653..06d62198 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -49,7 +49,7 @@ function addListenerAddComment(){ }) }); - await formResponseProcessToMain(response); + await formResponseProcess(response, `/post/${postId}`); }); } From 6097f9097cb5b72675f90461ba47df504e53860d Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 20:18:34 +0900 Subject: [PATCH 16/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=ED=91=9C=EC=8B=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 14 +++++ .../webserver/parse/DTO/CommentViewer.java | 4 ++ .../java/webserver/parse/DTO/PostViewer.java | 24 +++----- .../webserver/parse/RepeatDataReplacer.java | 32 +++++++++++ .../java/webserver/process/Processor.java | 27 +++++++-- src/main/java/webserver/route/Router.java | 6 +- src/main/resources/static/index.html | 55 +----------------- src/main/resources/static/main/index.html | 57 +------------------ 8 files changed, 90 insertions(+), 129 deletions(-) create mode 100644 src/main/java/webserver/parse/DTO/CommentViewer.java create mode 100644 src/main/java/webserver/parse/RepeatDataReplacer.java diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index c222e53f..d67e743c 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -28,6 +28,10 @@ public class Config { public static final String POST_ID_QUERY_NAME = "postId"; + public static final String REPEAT_FORMAT_PLACEHOLDER = "{{REPEAT}}"; + + public static final String NO_COMMENT = "댓글이 없습니다. 먼저 작성을 시작해주세요!"; + public static final String PAGE_HEADER_LOGIN = "
    " + "
      " + "
    • " + @@ -54,4 +58,14 @@ public class Config { " 회원 가입" + "
    • " + "
    "; + + public static final String COMMENT_REPEAT_FORMAT = "
  • \n" + + "
    \n" + + " \n" + + "

    {{{{REPEAT}}.authorName}}

    \n" + + "
    \n" + + "

    \n" + + " {{{{REPEAT}}.content}}\n" + + "

    \n" + + "
  • "; } diff --git a/src/main/java/webserver/parse/DTO/CommentViewer.java b/src/main/java/webserver/parse/DTO/CommentViewer.java new file mode 100644 index 00000000..e5372262 --- /dev/null +++ b/src/main/java/webserver/parse/DTO/CommentViewer.java @@ -0,0 +1,4 @@ +package webserver.parse.DTO; + +public record CommentViewer(String content, String authorName, String authorImagePath) { +} diff --git a/src/main/java/webserver/parse/DTO/PostViewer.java b/src/main/java/webserver/parse/DTO/PostViewer.java index 2b8934c8..86456a6d 100644 --- a/src/main/java/webserver/parse/DTO/PostViewer.java +++ b/src/main/java/webserver/parse/DTO/PostViewer.java @@ -3,23 +3,17 @@ import model.Post; import model.User; -public class PostViewer { - String authorName; - String authorImagePath; - String imagePath; - int likes; - int commentNum; - String content; - long postId; +public record PostViewer(String authorName, String authorImagePath, String imagePath, int likes, int commentNum, String content, long postId) { public PostViewer(Post post, User user, int commentNum){ - this.authorImagePath = user.getImagePath(); - this.authorName = user.getName(); - this.imagePath = post.postImagePath(); - this.likes = post.likes(); - this.commentNum = commentNum; - this.content = post.content(); - this.postId = post.postId(); + this(user.getName(), + user.getImagePath(), + post.postImagePath(), + post.likes(), + commentNum, + post.content(), + post.postId() + ); } @Override diff --git a/src/main/java/webserver/parse/RepeatDataReplacer.java b/src/main/java/webserver/parse/RepeatDataReplacer.java new file mode 100644 index 00000000..b5108a67 --- /dev/null +++ b/src/main/java/webserver/parse/RepeatDataReplacer.java @@ -0,0 +1,32 @@ +package webserver.parse; + +import common.Config; +import common.Utils; + +import java.util.Collection; + +public class RepeatDataReplacer { + String replacerName; + String format; + String noDataFormat; + + public RepeatDataReplacer(String replacerName, String format, String noDataFormat) { + this.replacerName = replacerName; + this.format = format; + this.noDataFormat = noDataFormat; + } + + public String repeatReplace(Collection objects, String template){ + StringBuilder dataInputs = new StringBuilder(); + StringBuilder tns; + StringBuilder result = new StringBuilder(template); + DataReplacer dataReplacer = new DataReplacer(replacerName); + for(Object object: objects){ + tns = new StringBuilder(format); + Utils.replaceAll(tns, Config.REPEAT_FORMAT_PLACEHOLDER, replacerName); + dataInputs.append(dataReplacer.replace(object, tns.toString())); + } + Utils.replaceAll(result, "{{" + this.replacerName + "}}", !objects.isEmpty() ?dataInputs.toString():noDataFormat); + return result.toString(); + } +} diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index db84024a..5796d01d 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -13,14 +13,17 @@ import webserver.http.Request; import webserver.http.RequestBody; import webserver.http.Response; +import webserver.parse.DTO.CommentViewer; import webserver.parse.DTO.PostViewer; import webserver.parse.PageReplacer; import webserver.parse.DataReplacer; import webserver.parse.PageStruct; +import webserver.parse.RepeatDataReplacer; import webserver.route.Router; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Collection; public class Processor { @@ -32,6 +35,8 @@ public class Processor { private static final DataReplacer userReplacer = new DataReplacer("user"); private static final DataReplacer postReplacer = new DataReplacer("post"); private static final PageStruct pageStruct = new PageStruct(); + private static final RepeatDataReplacer commentRepeatReplacer = new RepeatDataReplacer("comment", Config.COMMENT_REPEAT_FORMAT, Config.NO_COMMENT); + //private static final PageReplacer pageReplacer = new PageReplacer(); public Processor() { @@ -163,10 +168,19 @@ public Response process(Request simpleReq) { pageStruct.setState(simpleReq.path, user != null); String template = pageReplacer.replace(pageStruct, new String(body)); template = userReplacer.replace(user, template); - PostViewer postViewer = getPostViewer(simpleReq); - body = postReplacer.replace(postViewer, template).getBytes(); - if (postViewer == null && Router.needPostData(simpleReq.path)) { - body = getNoPostExceptionPage(simpleReq, user); + body = template.getBytes(StandardCharsets.UTF_8); + if (Router.needPostData(simpleReq.path)) { + PostViewer postViewer = getPostViewer(simpleReq); + template = postReplacer.replace(postViewer, template); + if(postViewer == null){ + template = new String(getNoPostExceptionPage(simpleReq, user)); + } + else if(Router.needCommentData(simpleReq.path)){ + Collection comments = getCommentViewers(postViewer); + + template = commentRepeatReplacer.repeatReplace(comments, template); + } + body = template.getBytes(StandardCharsets.UTF_8); } response = new Response(WebException.HTTPStatus.OK, body, Response.contentType(simpleReq.path)); @@ -200,4 +214,9 @@ private PostViewer getPostViewer(Request request) { if (author == null) return null; return new PostViewer(post, author, Database.findCommentsByPost(post.postId()).size()); } + + private Collection getCommentViewers(PostViewer postViewer){ + Collection comments = Database.findCommentsByPost(postViewer.postId()); + return comments.stream().map(comment -> new CommentViewer(comment.content(), postViewer.authorName(), postViewer.authorImagePath())).toList(); + } } diff --git a/src/main/java/webserver/route/Router.java b/src/main/java/webserver/route/Router.java index af19114d..4cd330e0 100644 --- a/src/main/java/webserver/route/Router.java +++ b/src/main/java/webserver/route/Router.java @@ -20,7 +20,11 @@ public static boolean needLogin(String path) { public static boolean needPostData(String path) { return (path.compareTo(Config.DEFAULT_PAGE_PATH) == 0) || (path.compareTo("/") == 0) - || (path.compareTo(Config.MAIN_PAGE_PATH) == 0); + || (path.compareTo(Config.MAIN_PAGE_PATH) == 0) || (path.startsWith(Config.COMMENT_PAGE_PATH_PREFIX)); + } + + public static boolean needCommentData(String path){ + return path.compareTo(Config.DEFAULT_PAGE_PATH) == 0 || path.compareTo(Config.MAIN_PAGE_PATH) == 0; } public void register(Request req, Function func) { diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index a71c6c0b..86375caa 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -51,60 +51,7 @@

      -
    • -
      - -

      account

      -
      -

      - 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 - 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 - 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 - 재판을 받지 아니한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 - 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 - 조직·직무범위 기타 필요한 사항은 법률로 정한다. -

      -
    • -
    • -
      - -

      account

      -
      -

      - 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 - 정한다. -

      -
    • - - - + {{comment}} diff --git a/src/main/resources/static/main/index.html b/src/main/resources/static/main/index.html index a71c6c0b..0febb54d 100644 --- a/src/main/resources/static/main/index.html +++ b/src/main/resources/static/main/index.html @@ -3,7 +3,7 @@ - + @@ -51,60 +51,7 @@

        -
      • -
        - -

        account

        -
        -

        - 군인 또는 군무원이 아닌 국민은 대한민국의 영역안에서는 중대한 - 군사상 기밀·초병·초소·유독음식물공급·포로·군용물에 관한 죄중 - 법률이 정한 경우와 비상계엄이 선포된 경우를 제외하고는 군사법원의 - 재판을 받지 아니한다. -

        -
      • -
      • -
        - -

        account

        -
        -

        - 대통령의 임기연장 또는 중임변경을 위한 헌법개정은 그 헌법개정 제안 - 당시의 대통령에 대하여는 효력이 없다. 민주평화통일자문회의의 - 조직·직무범위 기타 필요한 사항은 법률로 정한다. -

        -
      • -
      • -
        - -

        account

        -
        -

        - 민주평화통일자문회의의 조직·직무범위 기타 필요한 사항은 법률로 - 정한다. -

        -
      • - - - + {{comment}} From 6b43f72a67cb94beb0d62e4d8176efd859401981 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 20:27:53 +0900 Subject: [PATCH 17/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=8B=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=9D=98=20=EC=B5=9C?= =?UTF-8?q?=EC=86=8C=20=EA=B8=B8=EC=9D=B4=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 2 ++ .../UserExceptionConverter.java | 20 +++++++++++++++++++ .../java/webserver/process/UserProcessor.java | 4 ++++ 3 files changed, 26 insertions(+) diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index d67e743c..f98f5a46 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -4,6 +4,8 @@ public class Config { public static final String CRLF = "\r\n"; public static final String REPLACE_PLACEHOLDER = "page"; + public static final int MIN_USER_DATA_LENGTH = 4; + public static final String DEFAULT_PAGE_PATH = "/index.html"; public static final String MAIN_PAGE_PATH = "/main/index.html"; public static final String REGISTRATION_PAGE_PATH = "/registration/index.html"; diff --git a/src/main/java/customException/UserExceptionConverter.java b/src/main/java/customException/UserExceptionConverter.java index 39b091a0..49dde15c 100644 --- a/src/main/java/customException/UserExceptionConverter.java +++ b/src/main/java/customException/UserExceptionConverter.java @@ -21,6 +21,26 @@ public static WebException needUserId() { ); } + public static WebException tooShortUserId(){ + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "사용자 아이디는 4자 이상이어야 합니다." + ); + } + + public static WebException tooShortUserName(){ + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "사용자명은 4자 이상이어야 합니다." + ); + } + + public static WebException tooShortUserPassword(){ + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "비밀번호는 4자 이상이어야 합니다." + ); + } public static WebException needUserName() { return new WebException( diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index 00b5effc..253a4250 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -25,6 +25,10 @@ public byte[] createUser(Request request) { if (Database.findUserById(user.getUserId()) != null) throw UserExceptionConverter.conflictUserID(); if (Database.findUserByName(user.getName()) != null) throw UserExceptionConverter.conflictUserName(); + + if(user.getUserId().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserId(); + if(user.getName().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserName(); + if(user.getPassword().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserPassword(); Database.addUser(user); return user.toString().getBytes(); From 0e10e52f53782eb8fb5009df1162d78cd1d861b8 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 20:55:43 +0900 Subject: [PATCH 18/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20=EC=97=86=EB=8A=94=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EB=94=94=EC=9D=98=20=EA=B2=BD=EC=9A=B0=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=9C=A0=EB=8F=84=20=EC=B0=BD=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/script/alert.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index 06d62198..27162d90 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -1,6 +1,12 @@ async function formResponseProcessToMain(response) { await formResponseProcess(response, "./index.html") } +async function formResponseProcessToLogin(response) { + await formResponseProcess(response, "./login/index.html") +} +async function formResponseProcessToRegistration(response) { + await formResponseProcess(response, "./index.html") +} async function formResponseProcess(response, nextPath) { if(await alertCall(response)) return; @@ -68,6 +74,21 @@ document.getElementById("login")?.addEventListener("submit", async (e) => { password }) }); + if(response.status >= 400 && response.status < 500){ + const text = await response.text(); + if (text === '사용자 데이터가 존재하지 않습니다.') { + const goSignup = confirm( + '존재하지 않는 아이디입니다.\n회원 가입 하시겠습니까?' + ); + + if (goSignup) { + location.href = '/registration/index.html'; + } + return; + } + alert("에러 발생: " + text); + return; + } await formResponseProcessToMain(response); }); @@ -90,7 +111,7 @@ document.getElementById("registration")?.addEventListener("submit", async (e) => }) }); - await formResponseProcessToMain(response); + await formResponseProcessToLogin(response); }); document.getElementById("link_to_mypage")?.addEventListener("click", async (e) => { From e0760a269fd6a44a8225d5f38e47099a18de667c Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 20:59:12 +0900 Subject: [PATCH 19/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20=ED=8C=A8=EC=8A=A4=EC=9B=8C?= =?UTF-8?q?=EB=93=9C=EB=A7=8C=20=ED=8B=80=EB=A6=B0=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EC=9D=98=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/customException/UserExceptionConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/customException/UserExceptionConverter.java b/src/main/java/customException/UserExceptionConverter.java index 49dde15c..85e8335d 100644 --- a/src/main/java/customException/UserExceptionConverter.java +++ b/src/main/java/customException/UserExceptionConverter.java @@ -66,7 +66,7 @@ public static WebException notFoundUser() { public static WebException unAuthorized() { return new WebException( WebException.HTTPStatus.UNAUTHORIZED, - "로그인에 실패했습니다." + "비밀번호가 틀렸습니다." ); } From 5c898fed9b84b377d0f8bde9b5e66ef6c8034a82 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 21:39:51 +0900 Subject: [PATCH 20/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EA=B5=AC=ED=98=84[=EC=9D=B4=EB=A6=84,=20?= =?UTF-8?q?=ED=8C=A8=EC=8A=A4=EC=9B=8C=EB=93=9C]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserExceptionConverter.java | 22 +++++++++++++++--- src/main/java/db/Database.java | 15 ++++++++++++ src/main/java/model/User.java | 14 ++++++++--- .../java/webserver/process/Processor.java | 6 +++++ .../java/webserver/process/UserProcessor.java | 23 ++++++++++++++++--- src/main/resources/static/mypage/index.html | 21 ++++++----------- src/main/resources/static/script/alert.js | 21 +++++++++++++++++ 7 files changed, 99 insertions(+), 23 deletions(-) diff --git a/src/main/java/customException/UserExceptionConverter.java b/src/main/java/customException/UserExceptionConverter.java index 85e8335d..08c451e1 100644 --- a/src/main/java/customException/UserExceptionConverter.java +++ b/src/main/java/customException/UserExceptionConverter.java @@ -7,6 +7,7 @@ public static WebException conflictUserID() { "해당 아이디를 사용하는 회원이 존재합니다." ); } + public static WebException conflictUserName() { return new WebException( WebException.HTTPStatus.CONFLICT, @@ -21,21 +22,21 @@ public static WebException needUserId() { ); } - public static WebException tooShortUserId(){ + public static WebException tooShortUserId() { return new WebException( WebException.HTTPStatus.BAD_REQUEST, "사용자 아이디는 4자 이상이어야 합니다." ); } - public static WebException tooShortUserName(){ + public static WebException tooShortUserName() { return new WebException( WebException.HTTPStatus.BAD_REQUEST, "사용자명은 4자 이상이어야 합니다." ); } - public static WebException tooShortUserPassword(){ + public static WebException tooShortUserPassword() { return new WebException( WebException.HTTPStatus.BAD_REQUEST, "비밀번호는 4자 이상이어야 합니다." @@ -77,4 +78,19 @@ public static WebException needToLogin() { "/login" ); } + + + public static WebException passwordNotMatch() { + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "비밀번호가 일치하지 않습니다." + ); + } + + public static WebException failedUserUpdate() { + return new WebException( + WebException.HTTPStatus.INTERNAL_SERVER_ERROR, + "DB 내 사용자 데이터를 업데이트하는 것을 실패했습니다." + ); + } } diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index 04dc36e5..28147b23 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -292,4 +292,19 @@ public static int getPostLikes(long postId) { throw PostExceptionConverter.notFoundPost(); } } + + public static void updateUser(User user){ + String sql = "UPDATE users SET name = ?, password = ? WHERE user_id = ?"; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, user.getName()); + pstmt.setString(2, user.getPassword()); + pstmt.setString(3, user.getUserId()); + pstmt.execute(); + } + catch (SQLException e){ + logger.error(e.getMessage()); + throw UserExceptionConverter.failedUserUpdate(); + } + } } diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index ce2a689d..745a3b29 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -14,9 +14,9 @@ public User(String userId, String password, String name) { if (userId == null || password == null || name == null || userId.isBlank() || password.isBlank() || name.isBlank()) throw UserExceptionConverter.needUserData(); - this.userId = userId; - this.password = password; - this.name = name; + this.userId = userId.trim(); + this.password = password.trim(); + this.name = name.trim(); this.image = null; this.imagePath = Config.IMAGE_DEFAULT_PROFILE_NAME; } @@ -41,6 +41,14 @@ public void setImagePath(String path) { this.imagePath = path; } + public void setName(String name) { + this.name = name; + } + + public void setPassword(String password){ + this.password = password; + } + @Override public String toString() { return "User [userId=" + userId + ", password=" + password + ", name=" + name + "]"; diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 5796d01d..67064acf 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -98,6 +98,12 @@ public void init() { return response; }); + + router.register(new Request(Request.Method.POST, "/user/update"), request -> { + userProcessor.updateUser(request); + return new Response(WebException.HTTPStatus.OK, null, Response.ContentType.HTML); + }); + router.register(new Request(Request.Method.POST, "/post/create"), request -> { User user = userProcessor.getUserOrException(request); if (request.bodyParam.getOrDefault("content", null) == null) { diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index 253a4250..8457216b 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -26,9 +26,10 @@ public byte[] createUser(Request request) { if (Database.findUserById(user.getUserId()) != null) throw UserExceptionConverter.conflictUserID(); if (Database.findUserByName(user.getName()) != null) throw UserExceptionConverter.conflictUserName(); - if(user.getUserId().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserId(); - if(user.getName().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserName(); - if(user.getPassword().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserPassword(); + if (user.getUserId().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserId(); + if (user.getName().length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserName(); + if (user.getPassword().length() < Config.MIN_USER_DATA_LENGTH) + throw UserExceptionConverter.tooShortUserPassword(); Database.addUser(user); return user.toString().getBytes(); @@ -69,5 +70,21 @@ public void deleteUserSession(Request request) { Auth.deleteSession(SID); } + + public void updateUser(Request request) { + User user = getUserOrException(request); + if (request.bodyParam.get("userName") != null) { + String userName = request.bodyParam.get("userName").getContentString().trim(); + user.setName(userName); + } + if (request.bodyParam.get("password") != null && request.bodyParam.get("checkPassword") != null) { + String password = request.bodyParam.get("password").getContentString().trim(); + String checkPassword = request.bodyParam.get("checkPassword").getContentString().trim(); + if(password.compareTo(checkPassword) != 0) throw UserExceptionConverter.passwordNotMatch(); + user.setPassword(password); + } + + Database.updateUser(user); + } } diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index d3546e32..766dffbb 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -11,16 +11,7 @@
        - + {{page.header}}

        마이페이지

        @@ -40,14 +31,14 @@

        마이페이지

        -
        +

        닉네임

        + placeholder="{{user.name}}" + id="update-name"/>

        비밀번호

        @@ -55,6 +46,7 @@

        마이페이지

        class="input_textfield" placeholder="비밀번호를 변경할 경우 입력해주세요" autocomplete="current-password" + id="update-password" />
        @@ -64,13 +56,14 @@

        마이페이지

        type="password" placeholder="한 번 더 입력해주세요" autocomplete="current-password" + id="update-check-password" />
        diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index 27162d90..b7df7931 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -114,6 +114,27 @@ document.getElementById("registration")?.addEventListener("submit", async (e) => await formResponseProcessToLogin(response); }); +document.getElementById("user_update")?.addEventListener("submit", async (e) => { + e.preventDefault(); // 기본 submit 막기 + const userName = document.querySelector("#update-name").value; + const password = document.querySelector("#update-password").value; + const checkPassword = document.querySelector("#update-check-password").value; + + const response = await fetch("/user/update", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + userName, + password, + checkPassword + }) + }); + + await formResponseProcessToMain(response); +}); + document.getElementById("link_to_mypage")?.addEventListener("click", async (e) => { e.preventDefault(); // 기본 submit 막기 From a89bfcc4f28cc5028c906ca8f2e2719bb46d97ba Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 21:42:28 +0900 Subject: [PATCH 21/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=97=A4=EB=8D=94=20=EB=88=84=EB=9D=BD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/webserver/parse/PageStruct.java | 1 + src/main/resources/static/mypage/index.html | 129 +++++++++--------- 2 files changed, 66 insertions(+), 64 deletions(-) diff --git a/src/main/java/webserver/parse/PageStruct.java b/src/main/java/webserver/parse/PageStruct.java index 67276ea2..2c1dd014 100644 --- a/src/main/java/webserver/parse/PageStruct.java +++ b/src/main/java/webserver/parse/PageStruct.java @@ -31,5 +31,6 @@ private void init(){ headerContent.setPage(false, PageReplacer.PageType.MAIN, Config.PAGE_HEADER_NOT_LOGIN); headerContent.setPage(true,PageReplacer.PageType.ARTICLE, Config.PAGE_HEADER_LOGIN); headerContent.setPage(true,PageReplacer.PageType.COMMENT, Config.PAGE_HEADER_LOGIN); + headerContent.setPage(true,PageReplacer.PageType.MY_PAGE, Config.PAGE_HEADER_LOGIN); } } diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index 766dffbb..03c0f16f 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -1,76 +1,77 @@ - - - - - - - - -
        -
        - + + + + + + + + + +
        +
        + {{page.header}} -
        -
        +
        +

        마이페이지

        -
        -
        - -
        -
        -
        +
        +
        + +
        +
        +
        수정
        -
        -
        -
        -
        삭제
        -
        -
        -
        - -
        -

        닉네임

        - -
        -
        -

        비밀번호

        - -
        -
        -

        비밀번호 확인

        - +
        +
        +
        +
        삭제
        +
        +
        - - + type="submit" + > + 변경사항 저장 + +
        -
        - - +
        + + From f6baab9f5c6ac6fe82097fd54de482550ed5ac78 Mon Sep 17 00:00:00 2001 From: kimhji Date: Thu, 15 Jan 2026 21:51:36 +0900 Subject: [PATCH 22/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=ED=97=A4?= =?UTF-8?q?=EB=8D=94=20=EB=B2=94=EC=9C=84=20=EC=A6=9D=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 4 ++-- src/main/resources/static/script/alert.js | 26 +++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index f98f5a46..4a35c99a 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -37,10 +37,10 @@ public class Config { public static final String PAGE_HEADER_LOGIN = "
        " + "
          " + "
        • " + - " " + + " " + "
        • " + "
        • " + - " " + + " " + "
        • " + "
        • \n" + "글쓰기" + diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index b7df7931..4c0c19bf 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -1,15 +1,17 @@ async function formResponseProcessToMain(response) { await formResponseProcess(response, "./index.html") } + async function formResponseProcessToLogin(response) { await formResponseProcess(response, "./login/index.html") } + async function formResponseProcessToRegistration(response) { await formResponseProcess(response, "./index.html") } async function formResponseProcess(response, nextPath) { - if(await alertCall(response)) return; + if (await alertCall(response)) return; // 성공 if (response.status >= 200 && response.status < 300 && nextPath != null && nextPath.length > 0) { @@ -18,7 +20,7 @@ async function formResponseProcess(response, nextPath) { } async function getView(response) { - if(await alertCall(response)) return; + if (await alertCall(response)) return; const html = await response.text(); @@ -27,7 +29,7 @@ async function getView(response) { document.close(); } -async function alertCall(response){ +async function alertCall(response) { if (response.status >= 400 && response.status < 500) { const msg = await response.text(); alert("에러 발생: " + msg); @@ -36,7 +38,7 @@ async function alertCall(response){ return false; } -function addListenerAddComment(){ +function addListenerAddComment() { const btn = document.getElementById('create_comment_btn'); btn?.addEventListener('submit', async (e) => { @@ -74,7 +76,7 @@ document.getElementById("login")?.addEventListener("submit", async (e) => { password }) }); - if(response.status >= 400 && response.status < 500){ + if (response.status >= 400 && response.status < 500) { const text = await response.text(); if (text === '사용자 데이터가 존재하지 않습니다.') { const goSignup = confirm( @@ -135,14 +137,16 @@ document.getElementById("user_update")?.addEventListener("submit", async (e) => await formResponseProcessToMain(response); }); -document.getElementById("link_to_mypage")?.addEventListener("click", async (e) => { - e.preventDefault(); // 기본 submit 막기 +document.querySelectorAll(".link_to_mypage").forEach(el => { + el.addEventListener("click", async (e) => { + e.preventDefault(); // 기본 submit 막기 - const response = await fetch("/mypage", { - method: "GET" - }); + const response = await fetch("/mypage", { + method: "GET" + }); - await getView(response); + await getView(response); + }) }); document.getElementById("logout-btn")?.addEventListener("click", async (e) => { From 1630a9a82396467a3e1724507b596a1500075d59 Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 00:21:06 +0900 Subject: [PATCH 23/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20=EC=88=98=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/global.css | 8 +++++++ src/main/resources/static/mypage/index.html | 10 +++++++-- .../resources/static/script/extraEvent.js | 21 +++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/main/resources/static/global.css b/src/main/resources/static/global.css index 233d795b..4640e2fe 100644 --- a/src/main/resources/static/global.css +++ b/src/main/resources/static/global.css @@ -151,6 +151,14 @@ height: 200px; border-radius: 9999px; background: #d9d9d9; + object-fit: cover; + overflow: hidden; +} + +img { + width: 100%; + height: 100%; + object-fit: cover; } .profile_container { diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index 03c0f16f..1cd33c70 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -20,11 +20,16 @@

          마이페이지

          - +
          -
          수정
          +
          + + +
          @@ -73,5 +78,6 @@

          마이페이지

          + diff --git a/src/main/resources/static/script/extraEvent.js b/src/main/resources/static/script/extraEvent.js index 41ef72de..6eb8437f 100644 --- a/src/main/resources/static/script/extraEvent.js +++ b/src/main/resources/static/script/extraEvent.js @@ -1,6 +1,6 @@ function addListenerLikeAction(){ const btn = document.getElementById('post__like__btn'); - + if(btn == null) return; btn.addEventListener('click', async () => { const postId = btn.dataset.postId; if (!postId) return; @@ -18,5 +18,22 @@ function addListenerLikeAction(){ } +function addListenerSaveTempImage(){ + const imageInput = document.getElementById('form_profile_image'); + if(imageInput == null) return; + + imageInput.addEventListener('change', async (e) => { + const file = e.target.files[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = () => { + document.getElementById('user-profile-preview').src = reader.result; + }; + reader.readAsDataURL(file); + }); +} + -addListenerLikeAction(); \ No newline at end of file +addListenerLikeAction(); +addListenerSaveTempImage(); \ No newline at end of file From d33b8c73429d9433b890bd78db1f51a958e2e13f Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 00:36:46 +0900 Subject: [PATCH 24/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20=EC=82=AD=EC=A0=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/mypage/index.html | 4 ++-- src/main/resources/static/script/extraEvent.js | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index 1cd33c70..8a478932 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -25,7 +25,7 @@

          마이페이지

          -
          -
          삭제
          +
          삭제
          diff --git a/src/main/resources/static/script/extraEvent.js b/src/main/resources/static/script/extraEvent.js index 6eb8437f..24e5ebd7 100644 --- a/src/main/resources/static/script/extraEvent.js +++ b/src/main/resources/static/script/extraEvent.js @@ -34,6 +34,19 @@ function addListenerSaveTempImage(){ }); } +function addListenerRemoveTempImage(){ + const removeBtn = document.getElementById('remove_profile_image'); + const imageInput = document.getElementById('form_profile_image'); + const previewImg = document.getElementById('user-profile-preview'); + if(removeBtn == null || imageInput == null || previewImg == null) return; + + removeBtn.addEventListener('click', async (e) => { + imageInput.value = ""; + previewImg.src = "/image/profile/default.png"; + }); +} + addListenerLikeAction(); -addListenerSaveTempImage(); \ No newline at end of file +addListenerSaveTempImage(); +addListenerRemoveTempImage(); \ No newline at end of file From 5b855c7fd2576d63f9cfc0716cf38aac2a2793ca Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 01:05:02 +0900 Subject: [PATCH 25/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/User.java | 2 -- .../java/webserver/process/UserProcessor.java | 13 ++++++++++++ src/main/resources/static/mypage/index.html | 4 ++-- src/main/resources/static/script/alert.js | 20 +++++++++++-------- .../resources/static/script/extraEvent.js | 4 ++-- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index 745a3b29..0a9d63e3 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -7,7 +7,6 @@ public class User { private String userId; private String password; private String name; - private byte[] image; private String imagePath; public User(String userId, String password, String name) { @@ -17,7 +16,6 @@ public User(String userId, String password, String name) { this.userId = userId.trim(); this.password = password.trim(); this.name = name.trim(); - this.image = null; this.imagePath = Config.IMAGE_DEFAULT_PROFILE_NAME; } diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index 8457216b..616b9974 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -5,6 +5,7 @@ import common.Utils; import customException.UserExceptionConverter; import db.Database; +import db.ImageManager; import model.User; import webserver.http.Request; import webserver.http.RequestBody; @@ -83,8 +84,20 @@ public void updateUser(Request request) { if(password.compareTo(checkPassword) != 0) throw UserExceptionConverter.passwordNotMatch(); user.setPassword(password); } + RequestBody image = request.bodyParam.get("profileImage"); + if(image != null) { + switchProfileImage(image.getContent(), user); + } Database.updateUser(user); } + + private void switchProfileImage(byte[] image, User user) { + if(!user.getImagePath().endsWith(Config.IMAGE_DEFAULT_PROFILE_NAME)){ + //TODO:이미지 삭제 + } + String path = ImageManager.saveImageProfile(image); + user.setImagePath(path); + } } diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index 8a478932..ba5f17f2 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -25,10 +25,9 @@

          마이페이지

          -
          @@ -38,6 +37,7 @@

          마이페이지

          +

          닉네임

          const userName = document.querySelector("#update-name").value; const password = document.querySelector("#update-password").value; const checkPassword = document.querySelector("#update-check-password").value; + const imageInput = document.querySelector("#update-image"); + const file = imageInput.files[0]; + + const formData = new FormData(); + formData.append("userName", userName); + formData.append("password", password); + formData.append("checkPassword", checkPassword); + + if (file) { + formData.append("profileImage", file); // 이미지 추가 + } const response = await fetch("/user/update", { method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded" - }, - body: new URLSearchParams({ - userName, - password, - checkPassword - }) + body: formData }); await formResponseProcessToMain(response); diff --git a/src/main/resources/static/script/extraEvent.js b/src/main/resources/static/script/extraEvent.js index 24e5ebd7..caad2451 100644 --- a/src/main/resources/static/script/extraEvent.js +++ b/src/main/resources/static/script/extraEvent.js @@ -19,7 +19,7 @@ function addListenerLikeAction(){ } function addListenerSaveTempImage(){ - const imageInput = document.getElementById('form_profile_image'); + const imageInput = document.getElementById('update-image'); if(imageInput == null) return; imageInput.addEventListener('change', async (e) => { @@ -36,7 +36,7 @@ function addListenerSaveTempImage(){ function addListenerRemoveTempImage(){ const removeBtn = document.getElementById('remove_profile_image'); - const imageInput = document.getElementById('form_profile_image'); + const imageInput = document.getElementById('update-image'); const previewImg = document.getElementById('user-profile-preview'); if(removeBtn == null || imageInput == null || previewImg == null) return; From fa7130a894d29ae69c7649cbd3695ef6aacb504e Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 01:19:13 +0900 Subject: [PATCH 26/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20DB=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/Database.java | 20 ++++++++++++------- src/main/java/model/User.java | 10 ++++++++++ .../java/webserver/process/UserProcessor.java | 8 ++++++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/main/java/db/Database.java b/src/main/java/db/Database.java index 28147b23..0ad65460 100644 --- a/src/main/java/db/Database.java +++ b/src/main/java/db/Database.java @@ -25,7 +25,8 @@ public static void init(){ stmt.execute("CREATE TABLE users (\n" + " user_id VARCHAR(50) PRIMARY KEY,\n" + " password VARCHAR(255) NOT NULL,\n" + - " name VARCHAR(50) NOT NULL UNIQUE\n" + + " name VARCHAR(50) NOT NULL UNIQUE,\n" + + " image_path VARCHAR(255) NOT NULL\n" + ");\n"); stmt.execute("CREATE TABLE posts (\n" + @@ -54,12 +55,13 @@ public static void init(){ public static void addUser(User user) { try { - String sql = "INSERT INTO users(user_id, password, name) VALUES (?, ?, ?)"; + String sql = "INSERT INTO users(user_id, password, name, image_path) VALUES (?, ?, ?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, user.getUserId()); pstmt.setString(2, user.getPassword()); pstmt.setString(3, user.getName()); + pstmt.setString(4, user.getImagePath()); pstmt.executeUpdate(); } @@ -84,7 +86,8 @@ public static User findUserById(String userId) { return new User( result.getString("user_id"), result.getString("password"), - result.getString("name") + result.getString("name"), + result.getString("image_path") ); } catch (SQLException e){ @@ -108,7 +111,8 @@ public static User findUserByName(String name) { return new User( result.getString("user_id"), result.getString("password"), - result.getString("name") + result.getString("name"), + result.getString("image_path") ); } catch (SQLException e){ @@ -127,7 +131,8 @@ public static Collection findAllUser() { while(result.next()){ users.add(new User(result.getString("user_id"), result.getString("password"), - result.getString("name"))); + result.getString("name"), + result.getString("image_path"))); } return users; } @@ -294,12 +299,13 @@ public static int getPostLikes(long postId) { } public static void updateUser(User user){ - String sql = "UPDATE users SET name = ?, password = ? WHERE user_id = ?"; + String sql = "UPDATE users SET name = ?, password = ?, image_path = ? WHERE user_id = ?"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, user.getName()); pstmt.setString(2, user.getPassword()); - pstmt.setString(3, user.getUserId()); + pstmt.setString(3, user.getImagePath()); + pstmt.setString(4, user.getUserId()); pstmt.execute(); } catch (SQLException e){ diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index 0a9d63e3..c579bbe5 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -19,6 +19,16 @@ public User(String userId, String password, String name) { this.imagePath = Config.IMAGE_DEFAULT_PROFILE_NAME; } + public User(String userId, String password, String name, String imagePath) { + if (userId == null || password == null || name == null + || userId.isBlank() || password.isBlank() || name.isBlank()) + throw UserExceptionConverter.needUserData(); + this.userId = userId.trim(); + this.password = password.trim(); + this.name = name.trim(); + this.imagePath = imagePath.trim(); + } + public String getUserId() { return userId; } diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index 616b9974..a1f6f725 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -74,14 +74,18 @@ public void deleteUserSession(Request request) { public void updateUser(Request request) { User user = getUserOrException(request); - if (request.bodyParam.get("userName") != null) { + if (request.bodyParam.get("userName") != null && !request.bodyParam.get("userName").toString().isBlank()) { String userName = request.bodyParam.get("userName").getContentString().trim(); + if (userName.length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserName(); user.setName(userName); } - if (request.bodyParam.get("password") != null && request.bodyParam.get("checkPassword") != null) { + if (request.bodyParam.get("password") != null && request.bodyParam.get("checkPassword") != null + && !request.bodyParam.get("password").toString().isBlank()) { String password = request.bodyParam.get("password").getContentString().trim(); String checkPassword = request.bodyParam.get("checkPassword").getContentString().trim(); if(password.compareTo(checkPassword) != 0) throw UserExceptionConverter.passwordNotMatch(); + if (password.length() < Config.MIN_USER_DATA_LENGTH) + throw UserExceptionConverter.tooShortUserPassword(); user.setPassword(password); } RequestBody image = request.bodyParam.get("profileImage"); From eb36a1885a9ef372c63bdb6efb7c98dafe6e13f0 Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 01:24:54 +0900 Subject: [PATCH 27/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=8B=9C=20=EA=B8=B0=EC=A1=B4=EC=97=90=20=EB=B3=B4?= =?UTF-8?q?=EA=B4=80=ED=95=98=EB=8D=98=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/db/ImageManager.java | 11 ++++++++++- src/main/java/webserver/process/UserProcessor.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/db/ImageManager.java b/src/main/java/db/ImageManager.java index 84ce871d..7d1abb43 100644 --- a/src/main/java/db/ImageManager.java +++ b/src/main/java/db/ImageManager.java @@ -35,7 +35,6 @@ private static String saveImage(String inputBasePath, byte[] imageBytes) { dir.mkdirs(); } - // 파일명 생성 String timestamp = LocalDateTime.now().format(FORMATTER); String fileName = timestamp + ".png"; @@ -78,4 +77,14 @@ private static byte[] readImage(String inputBasePath, String path){ return image; } + + public static boolean deleteProfileFile(String path) { + File file = new File(baseProfilePath, path); + + if (!file.exists() || !file.isFile()) { + return false; + } + + return file.delete(); + } } diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index a1f6f725..df6793d0 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -98,7 +98,7 @@ public void updateUser(Request request) { private void switchProfileImage(byte[] image, User user) { if(!user.getImagePath().endsWith(Config.IMAGE_DEFAULT_PROFILE_NAME)){ - //TODO:이미지 삭제 + ImageManager.deleteProfileFile(user.getImagePath()); } String path = ImageManager.saveImageProfile(image); user.setImagePath(path); From d5cb5bfd9dd48b7746af34a56e017e8b76faaf4a Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 02:22:24 +0900 Subject: [PATCH 28/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 1 + .../java/webserver/process/UserProcessor.java | 18 +++++++++++++++++- src/main/resources/static/script/alert.js | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index 4a35c99a..097e1a3b 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -27,6 +27,7 @@ public class Config { public static final String IMAGE_BASE_PATH = "./src/main/resources/uploads"; public static final String IMAGE_PROFILE_BASE_PATH = "./src/main/resources/uploads/profiles"; public static final String IMAGE_DEFAULT_PROFILE_NAME = "default.png"; + public static final String IMAGE_DEFAULT_PROFILE_API = "/image/profile/default.png"; public static final String POST_ID_QUERY_NAME = "postId"; diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index df6793d0..c593dfb7 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -79,6 +79,7 @@ public void updateUser(Request request) { if (userName.length() < Config.MIN_USER_DATA_LENGTH) throw UserExceptionConverter.tooShortUserName(); user.setName(userName); } + if (request.bodyParam.get("password") != null && request.bodyParam.get("checkPassword") != null && !request.bodyParam.get("password").toString().isBlank()) { String password = request.bodyParam.get("password").getContentString().trim(); @@ -88,11 +89,26 @@ public void updateUser(Request request) { throw UserExceptionConverter.tooShortUserPassword(); user.setPassword(password); } + RequestBody image = request.bodyParam.get("profileImage"); + RequestBody imagePath = request.bodyParam.get("previewImg"); if(image != null) { switchProfileImage(image.getContent(), user); } - + else if(imagePath != null){ + try { + String raw = imagePath.getContentString(); + String path = new java.net.URL(raw).getPath(); + + if (Config.IMAGE_DEFAULT_PROFILE_API.equals(path)) { + switchProfileImage( + ImageManager.readImageProfile(Config.IMAGE_DEFAULT_PROFILE_NAME), + user + ); + } + } catch (Exception e) { + } + } Database.updateUser(user); } diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index e1685655..3b494cfe 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -122,12 +122,14 @@ document.getElementById("user_update")?.addEventListener("submit", async (e) => const password = document.querySelector("#update-password").value; const checkPassword = document.querySelector("#update-check-password").value; const imageInput = document.querySelector("#update-image"); + const previewImg = document.getElementById('user-profile-preview'); const file = imageInput.files[0]; const formData = new FormData(); formData.append("userName", userName); formData.append("password", password); formData.append("checkPassword", checkPassword); + formData.append("previewImg", previewImg.src); if (file) { formData.append("profileImage", file); // 이미지 추가 From b2d17e8b6f5f659ee3ae44e5431664b1e50456a7 Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 02:24:29 +0900 Subject: [PATCH 29/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=A6=84=20=EB=AF=B8=EB=A6=AC=20?= =?UTF-8?q?=EC=9D=B8=ED=92=8B=EC=97=90=20=EB=84=A3=EC=96=B4=EB=86=93?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/mypage/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/static/mypage/index.html b/src/main/resources/static/mypage/index.html index ba5f17f2..d2c64d8d 100644 --- a/src/main/resources/static/mypage/index.html +++ b/src/main/resources/static/mypage/index.html @@ -44,7 +44,9 @@

          마이페이지

          class="input_textfield" autocomplete="username" placeholder="{{user.name}}" - id="update-name"/> + id="update-name" + value="{{user.name}}" + />

          비밀번호

          From de2def49b7cb68f26c06188de80a4c52698536af Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 02:37:43 +0900 Subject: [PATCH 30/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=AC=EB=B6=80=EB=A7=8C=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/customException/PostExceptionConverter.java | 7 +++++++ src/main/java/webserver/process/Processor.java | 9 +++++---- src/main/resources/static/script/alert.js | 11 ++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/customException/PostExceptionConverter.java b/src/main/java/customException/PostExceptionConverter.java index f470d50a..35246997 100644 --- a/src/main/java/customException/PostExceptionConverter.java +++ b/src/main/java/customException/PostExceptionConverter.java @@ -15,6 +15,13 @@ public static WebException badContentPost() { ); } + public static WebException badFileContentPost() { + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "포스트의 이미지가 존재하지 않습니다." + ); + } + public static WebException badPostId() { return new WebException( WebException.HTTPStatus.BAD_REQUEST, diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 67064acf..877548e1 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -106,12 +106,13 @@ public void init() { router.register(new Request(Request.Method.POST, "/post/create"), request -> { User user = userProcessor.getUserOrException(request); - if (request.bodyParam.getOrDefault("content", null) == null) { - throw PostExceptionConverter.badContentPost(); + if (request.bodyParam.get("image") == null) { + throw PostExceptionConverter.badFileContentPost(); } - Post post = new Post(request.bodyParam.getOrDefault("image", new RequestBody("")).getContent(), + System.out.println("!!!!"+request.bodyParam.get("image").getContent()); + Post post = new Post(request.bodyParam.get("image").getContent(), user.getUserId(), - request.bodyParam.get("content").toString()); + request.bodyParam.getOrDefault("content", new RequestBody("")).toString()); Database.addPost(post); return new Response(WebException.HTTPStatus.OK, null, Response.ContentType.PLAIN_TEXT); }); diff --git a/src/main/resources/static/script/alert.js b/src/main/resources/static/script/alert.js index 3b494cfe..16e6e517 100644 --- a/src/main/resources/static/script/alert.js +++ b/src/main/resources/static/script/alert.js @@ -166,10 +166,15 @@ document.getElementById("logout-btn")?.addEventListener("click", async (e) => { }); document.getElementById("post")?.addEventListener("submit", async (e) => { - e.preventDefault(); // 기본 submit 막기 - + e.preventDefault(); const form = e.target; - const formData = new FormData(form); + const formData = new FormData(); + formData.append("content", form.querySelector("#content").value); + + const fileInput = form.querySelector('input[name="image"]'); + if (fileInput.files.length > 0) { + formData.append("image", fileInput.files[0]); + } const response = await fetch("/post/create", { method: "POST", From 272a92ed0c606c952b8cf9ca2419c0aacec278e0 Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 02:49:36 +0900 Subject: [PATCH 31/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20png=EC=99=80=20JPE?= =?UTF-8?q?G=20=EB=A7=8C=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 5 ++++ src/main/java/common/Utils.java | 27 +++++++++++++++++++ .../customException/DBExceptionConverter.java | 7 +++++ .../java/webserver/process/Processor.java | 4 ++- .../process/StaticFileProcessor.java | 8 ++++++ .../java/webserver/process/UserProcessor.java | 3 +++ 6 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index 097e1a3b..093bb46b 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -35,6 +35,11 @@ public class Config { public static final String NO_COMMENT = "댓글이 없습니다. 먼저 작성을 시작해주세요!"; + public static final String IMAGE_TYPE_UNKNOWN = "unknown"; + public static final String IMAGE_TYPE_PNG = "png"; + public static final String IMAGE_TYPE_JPEG = "JPEG"; + + public static final String PAGE_HEADER_LOGIN = "
          " + "
            " + "
          • " + diff --git a/src/main/java/common/Utils.java b/src/main/java/common/Utils.java index ba753de2..8fc4ce70 100644 --- a/src/main/java/common/Utils.java +++ b/src/main/java/common/Utils.java @@ -120,4 +120,31 @@ public static List getBothSplit (byte[] origin, byte[] splitter){ } return null; } + + public static String detectImageType(byte[] data) { + if (data.length < 4) return Config.IMAGE_TYPE_UNKNOWN; + + if ((data[0] & 0xFF) == 0xFF && + (data[1] & 0xFF) == 0xD8 && + (data[2] & 0xFF) == 0xFF) { + return Config.IMAGE_TYPE_JPEG; + } + + if ((data[0] & 0xFF) == 0x89 && + (data[1] & 0xFF) == 0x50 && + (data[2] & 0xFF) == 0x4E && + (data[3] & 0x47) == 0x47) { + return Config.IMAGE_TYPE_PNG; + } + +// // GIF +// if ((data[0] & 0xFF) == 0x47 && +// (data[1] & 0xFF) == 0x49 && +// (data[2] & 0xFF) == 0x46) { +// return "GIF"; +// } + + return Config.IMAGE_TYPE_UNKNOWN; + } + } diff --git a/src/main/java/customException/DBExceptionConverter.java b/src/main/java/customException/DBExceptionConverter.java index 7d37c880..4e73a625 100644 --- a/src/main/java/customException/DBExceptionConverter.java +++ b/src/main/java/customException/DBExceptionConverter.java @@ -64,4 +64,11 @@ public static WebException failToFindComment() { "댓글 데이터를 DB에서 탐색하는 과정에서 에러가 발생했습니다." ); } + + public static WebException notAppliedImageType() { + return new WebException( + WebException.HTTPStatus.BAD_REQUEST, + "png 파일과 jpeg 파일만 지원합니다." + ); + } } diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 877548e1..49a136a9 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -25,6 +25,8 @@ import java.nio.charset.StandardCharsets; import java.util.Collection; +import static webserver.process.StaticFileProcessor.checkImageType; + public class Processor { private static final Router router = new Router(); @@ -109,7 +111,7 @@ public void init() { if (request.bodyParam.get("image") == null) { throw PostExceptionConverter.badFileContentPost(); } - System.out.println("!!!!"+request.bodyParam.get("image").getContent()); + checkImageType(request.bodyParam.get("image").getContent()); Post post = new Post(request.bodyParam.get("image").getContent(), user.getUserId(), request.bodyParam.getOrDefault("content", new RequestBody("")).toString()); diff --git a/src/main/java/webserver/process/StaticFileProcessor.java b/src/main/java/webserver/process/StaticFileProcessor.java index 04a0c5cf..693471a7 100644 --- a/src/main/java/webserver/process/StaticFileProcessor.java +++ b/src/main/java/webserver/process/StaticFileProcessor.java @@ -1,5 +1,8 @@ package webserver.process; +import common.Config; +import common.Utils; +import customException.DBExceptionConverter; import customException.WebStatusConverter; import webserver.http.Request; @@ -18,6 +21,11 @@ public static byte[] processReq(Request simpleReq) { return result; } + static public void checkImageType(byte[] data) { + String type = Utils.detectImageType(data); + if(type.compareTo(Config.IMAGE_TYPE_UNKNOWN) == 0) throw DBExceptionConverter.notAppliedImageType(); + } + static public void setBasePath(String basePath) { StaticFileProcessor.basePath = basePath; } diff --git a/src/main/java/webserver/process/UserProcessor.java b/src/main/java/webserver/process/UserProcessor.java index c593dfb7..76716715 100644 --- a/src/main/java/webserver/process/UserProcessor.java +++ b/src/main/java/webserver/process/UserProcessor.java @@ -12,6 +12,8 @@ import java.util.Optional; +import static webserver.process.StaticFileProcessor.checkImageType; + public class UserProcessor { public byte[] createUser(Request request) { User user = new User(Optional.ofNullable(request.bodyParam.get("userId")) @@ -93,6 +95,7 @@ public void updateUser(Request request) { RequestBody image = request.bodyParam.get("profileImage"); RequestBody imagePath = request.bodyParam.get("previewImg"); if(image != null) { + checkImageType(image.getContent()); switchProfileImage(image.getContent(), user); } else if(imagePath != null){ From ddf37043ec7235e8df498cf2282fdc82735fe5c9 Mon Sep 17 00:00:00 2001 From: kimhji Date: Fri, 16 Jan 2026 03:39:31 +0900 Subject: [PATCH 32/36] =?UTF-8?q?feat[7=EB=8B=A8=EA=B3=84]:=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=ED=8E=BC=EC=B3=90=EB=B3=B4=EA=B8=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=8F=99=EC=A0=81=20=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/Config.java | 5 +++++ .../webserver/parse/RepeatDataReplacer.java | 17 +++++++++++++++++ src/main/java/webserver/process/Processor.java | 15 +++++++++------ src/main/resources/static/index.html | 4 +--- src/main/resources/static/main/index.html | 6 ++---- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/main/java/common/Config.java b/src/main/java/common/Config.java index 093bb46b..b76cba48 100644 --- a/src/main/java/common/Config.java +++ b/src/main/java/common/Config.java @@ -39,6 +39,7 @@ public class Config { public static final String IMAGE_TYPE_PNG = "png"; public static final String IMAGE_TYPE_JPEG = "JPEG"; + public static final int DEFAULT_COMMENT_COUNT = 3; public static final String PAGE_HEADER_LOGIN = "
            " + "
              " + @@ -76,4 +77,8 @@ public class Config { " {{{{REPEAT}}.content}}\n" + "

              \n" + " "; + + public static final String COMMENT_WANT_TO_SEE_MORE = ""; } diff --git a/src/main/java/webserver/parse/RepeatDataReplacer.java b/src/main/java/webserver/parse/RepeatDataReplacer.java index b5108a67..d53eec7e 100644 --- a/src/main/java/webserver/parse/RepeatDataReplacer.java +++ b/src/main/java/webserver/parse/RepeatDataReplacer.java @@ -29,4 +29,21 @@ public String repeatReplace(Collection objects, String template){ Utils.replaceAll(result, "{{" + this.replacerName + "}}", !objects.isEmpty() ?dataInputs.toString():noDataFormat); return result.toString(); } + + public String repeatReplace(Collection objects, String template, int count){ + StringBuilder dataInputs = new StringBuilder(); + StringBuilder tns; + StringBuilder result = new StringBuilder(template); + DataReplacer dataReplacer = new DataReplacer(replacerName); + int i = 0; + for(Object object: objects){ + tns = new StringBuilder(format); + Utils.replaceAll(tns, Config.REPEAT_FORMAT_PLACEHOLDER, replacerName); + dataInputs.append(dataReplacer.replace(object, tns.toString())); + i++; + if(i >= count){ break; } + } + Utils.replaceAll(result, "{{" + this.replacerName + "}}", !objects.isEmpty() ?dataInputs.toString():noDataFormat); + return result.toString(); + } } diff --git a/src/main/java/webserver/process/Processor.java b/src/main/java/webserver/process/Processor.java index 49a136a9..98df7cd5 100644 --- a/src/main/java/webserver/process/Processor.java +++ b/src/main/java/webserver/process/Processor.java @@ -1,6 +1,7 @@ package webserver.process; import common.Config; +import common.Utils; import customException.CommentExceptionConverter; import customException.PostExceptionConverter; import customException.WebException; @@ -176,21 +177,23 @@ public Response process(Request simpleReq) { if (body != null) { pageStruct.setState(simpleReq.path, user != null); String template = pageReplacer.replace(pageStruct, new String(body)); - template = userReplacer.replace(user, template); - body = template.getBytes(StandardCharsets.UTF_8); if (Router.needPostData(simpleReq.path)) { PostViewer postViewer = getPostViewer(simpleReq); - template = postReplacer.replace(postViewer, template); if(postViewer == null){ template = new String(getNoPostExceptionPage(simpleReq, user)); } else if(Router.needCommentData(simpleReq.path)){ Collection comments = getCommentViewers(postViewer); - - template = commentRepeatReplacer.repeatReplace(comments, template); + StringBuilder sb = new StringBuilder(template); + Utils.replaceAll(sb, "{{expand_comment_btn}}", + postViewer.commentNum()>Config.DEFAULT_COMMENT_COUNT?Config.COMMENT_WANT_TO_SEE_MORE:""); + template = sb.toString(); + template = commentRepeatReplacer.repeatReplace(comments, template, Config.DEFAULT_COMMENT_COUNT); } - body = template.getBytes(StandardCharsets.UTF_8); + template = postReplacer.replace(postViewer, template); } + template = userReplacer.replace(user, template); + body = template.getBytes(StandardCharsets.UTF_8); response = new Response(WebException.HTTPStatus.OK, body, Response.contentType(simpleReq.path)); } diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 86375caa..f5e03293 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -52,9 +52,7 @@
              {{comment}} - + {{expand_comment_btn}}
            {{comment}} - + {{expand_comment_btn}}