diff --git a/.gitignore b/.gitignore index 6fbb1a503..94da6c9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -197,3 +197,5 @@ gradle-app.setting *.hprof # End of https://www.toptal.com/developers/gitignore/api/gradle,intellij,java,macos + +**/uploads \ No newline at end of file diff --git a/src/main/java/db/ArticleRepository.java b/src/main/java/db/ArticleRepository.java index 48043be93..2d53c9c89 100644 --- a/src/main/java/db/ArticleRepository.java +++ b/src/main/java/db/ArticleRepository.java @@ -3,8 +3,20 @@ import model.Article; import java.util.List; +import java.util.Optional; public interface ArticleRepository { Article save(Article article); + List
findTopNLessThanByIdDecreasingOrder(int limit, long id); + + Optional
findById(long id); + + void update(Article article); + + Optional
findLatest(); + + Optional
findNext(Long id); + + Optional
findPrev(Long id); } diff --git a/src/main/java/db/ArticleRepositoryImpl.java b/src/main/java/db/ArticleRepositoryImpl.java index 9c4247632..3a840f341 100644 --- a/src/main/java/db/ArticleRepositoryImpl.java +++ b/src/main/java/db/ArticleRepositoryImpl.java @@ -7,6 +7,7 @@ import java.sql.*; import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class ArticleRepositoryImpl implements ArticleRepository { private static final String JDBC_URL = "jdbc:h2:tcp://localhost/~/h2-db/was-be"; @@ -15,7 +16,7 @@ public class ArticleRepositoryImpl implements ArticleRepository { private static final Logger logger = LoggerFactory.getLogger(ArticleRepositoryImpl.class); public Article save(Article article) { - String sql = "insert into article_tbl(creatorId, title, content) values(?, ?, ?)"; + String sql = "insert into article_tbl(creatorId, title, content, image_url) values(?, ?, ?, ?)"; try ( Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); PreparedStatement pstmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); @@ -23,6 +24,7 @@ public Article save(Article article) { pstmt.setLong(1, article.getCreatorId()); pstmt.setString(2, article.getTitle()); pstmt.setString(3, article.getContent()); + pstmt.setString(4, article.getImageUrl()); pstmt.executeUpdate(); @@ -41,7 +43,7 @@ public Article save(Article article) { @Override public List
findTopNLessThanByIdDecreasingOrder(int limit, long id) { - String sql = "select id, creatorId, title, content from article_tbl where id < ? order by id desc limit ?"; + String sql = "select id, creatorId, title, content, image_url, like_count from article_tbl where id < ? order by id desc limit ?"; try ( Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); @@ -58,7 +60,9 @@ public List
findTopNLessThanByIdDecreasingOrder(int limit, long id) { long creatorId = rs.getLong("creatorId"); String title = rs.getString("title"); String content = rs.getString("content"); - articles.add(new Article(articleId, creatorId, title, content)); + String imageUrl = rs.getString("image_url"); + long likeCount = rs.getLong("like_count"); + articles.add(new Article(articleId, creatorId, title, content, imageUrl, likeCount)); } } return articles; @@ -67,4 +71,159 @@ public List
findTopNLessThanByIdDecreasingOrder(int limit, long id) { throw new RuntimeException(e); } } + + @Override + public Optional
findById(long id) { + String sql = "select id, creatorId, title, content, image_url, like_count " + + "from article_tbl where id = ?"; + + try ( + Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); + PreparedStatement pstmt = con.prepareStatement(sql); + ) { + pstmt.setLong(1, id); + + try (ResultSet rs = pstmt.executeQuery()) { + if (!rs.next()) { + return Optional.empty(); + } + + Article article = new Article( + rs.getLong("id"), + rs.getLong("creatorId"), + rs.getString("title"), + rs.getString("content"), + rs.getString("image_url"), + rs.getLong("like_count") + ); + + return Optional.of(article); + } + } catch (SQLException e) { + logger.error("Article 조회 실패 id={}", id, e); + throw new RuntimeException(e); + } + } + + @Override + public void update(Article article) { + String sql = "update article_tbl " + + "set title = ?, content = ?, image_url = ?, like_count = ? " + + "where id = ?"; + + try ( + Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); + PreparedStatement pstmt = con.prepareStatement(sql); + ) { + pstmt.setString(1, article.getTitle()); + pstmt.setString(2, article.getContent()); + pstmt.setString(3, article.getImageUrl()); + pstmt.setLong(4, article.getLikeCount()); + pstmt.setLong(5, article.getId()); + + int updatedRows = pstmt.executeUpdate(); + if (updatedRows == 0) { + throw new SQLException("업데이트 실패 - 존재하지 않는 article id=" + article.getId()); + } + } catch (SQLException e) { + logger.error("Article 업데이트 실패 id={}", article.getId(), e); + throw new RuntimeException(e); + } + } + + @Override + public Optional
findLatest() { + String sql = "select id, creatorId, title, content, image_url, like_count " + + "from article_tbl order by id desc limit 1"; + + try ( + Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); + PreparedStatement pstmt = con.prepareStatement(sql); + ResultSet rs = pstmt.executeQuery(); + ) { + if (!rs.next()) { + return Optional.empty(); + } + + Article article = new Article( + rs.getLong("id"), + rs.getLong("creatorId"), + rs.getString("title"), + rs.getString("content"), + rs.getString("image_url"), + rs.getLong("like_count") + ); + + return Optional.of(article); + } catch (SQLException e) { + logger.error("최신 Article 조회 실패", e); + throw new RuntimeException(e); + } + } + + @Override + public Optional
findNext(Long id) { + String sql = "select id, creatorId, title, content, image_url, like_count " + + "from article_tbl where id > ? order by id asc limit 1"; + + try ( + Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); + PreparedStatement pstmt = con.prepareStatement(sql); + ) { + pstmt.setLong(1, id); + + try (ResultSet rs = pstmt.executeQuery()) { + if (!rs.next()) { + return Optional.empty(); + } + + Article article = new Article( + rs.getLong("id"), + rs.getLong("creatorId"), + rs.getString("title"), + rs.getString("content"), + rs.getString("image_url"), + rs.getLong("like_count") + ); + + return Optional.of(article); + } + } catch (SQLException e) { + logger.error("다음 Article 조회 실패 id={}", id, e); + throw new RuntimeException(e); + } + } + + @Override + public Optional
findPrev(Long id) { + String sql = "select id, creatorId, title, content, image_url, like_count " + + "from article_tbl where id < ? order by id desc limit 1"; + + try ( + Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); + PreparedStatement pstmt = con.prepareStatement(sql); + ) { + pstmt.setLong(1, id); + + try (ResultSet rs = pstmt.executeQuery()) { + if (!rs.next()) { + return Optional.empty(); + } + + Article article = new Article( + rs.getLong("id"), + rs.getLong("creatorId"), + rs.getString("title"), + rs.getString("content"), + rs.getString("image_url"), + rs.getLong("like_count") + ); + + return Optional.of(article); + } + } catch (SQLException e) { + logger.error("이전 Article 조회 실패 id={}", id, e); + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/db/UserRepository.java b/src/main/java/db/UserRepository.java index ca9d999bc..c31d4365c 100644 --- a/src/main/java/db/UserRepository.java +++ b/src/main/java/db/UserRepository.java @@ -9,4 +9,5 @@ public interface UserRepository { User save(User user); Optional findByEmail(String email); Optional findById(Long id); + User update(User user); } diff --git a/src/main/java/db/UserRepositoryImpl.java b/src/main/java/db/UserRepositoryImpl.java index f215c6b16..c8d2ea5d0 100644 --- a/src/main/java/db/UserRepositoryImpl.java +++ b/src/main/java/db/UserRepositoryImpl.java @@ -14,7 +14,7 @@ public class UserRepositoryImpl implements UserRepository { private static final Logger logger = LoggerFactory.getLogger(UserRepository.class); public User save(User user) { - String sql = "insert into user_tbl(password, nickname, email) values(?, ?, ?)"; + String sql = "insert into user_tbl(password, nickname, email, image_url) values(?, ?, ?, ?)"; try ( Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); @@ -25,6 +25,7 @@ public User save(User user) { pstmt.setString(1, user.getPassword()); pstmt.setString(2, user.getName()); pstmt.setString(3, user.getEmail()); + pstmt.setString(4, user.getImageUrl()); pstmt.executeUpdate(); @@ -43,7 +44,7 @@ public User save(User user) { } public Optional findByEmail(String email) { - String sql = "select id, password, nickname from user_tbl where email = ?"; + String sql = "select id, password, nickname, image_url from user_tbl where email = ?"; try ( Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); @@ -61,7 +62,8 @@ public Optional findByEmail(String email) { long id = rs.getLong("id"); String password = rs.getString("password"); String nickname = rs.getString("nickname"); - user = new User(id, password, nickname, email); + String imageUrl = rs.getString("image_url"); + user = new User(id, password, nickname, email, imageUrl); } return Optional.of(user); @@ -73,7 +75,7 @@ public Optional findByEmail(String email) { @Override public Optional findById(Long id) { - String sql = "select password, nickname, email from user_tbl where id = ?"; + String sql = "select password, nickname, email, image_url from user_tbl where id = ?"; try ( Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); @@ -91,7 +93,8 @@ public Optional findById(Long id) { String password = rs.getString("password"); String nickname = rs.getString("nickname"); String email = rs.getString("email"); - user = new User(id, password, nickname, email); + String imageUrl = rs.getString("image_url"); + user = new User(id, password, nickname, email, imageUrl); } return Optional.of(user); @@ -100,4 +103,34 @@ public Optional findById(Long id) { throw new RuntimeException(e); } } + + @Override + public User update(User user) { + String sql = "update user_tbl set password = ?, nickname = ?, email = ?, image_url = ? where id = ?"; + + try ( + Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD); + PreparedStatement pstmt = con.prepareStatement(sql); + ) { + logger.debug("DB 연결 성공"); + + pstmt.setString(1, user.getPassword()); + pstmt.setString(2, user.getName()); + pstmt.setString(3, user.getEmail()); + pstmt.setString(4, user.getImageUrl()); + pstmt.setLong(5, user.getId()); + + int updatedRows = pstmt.executeUpdate(); + + if (updatedRows == 0) { + throw new SQLException("업데이트 실패: 해당 id 없음"); + } + + return user; + } catch (SQLException e) { + logger.error("DB 업데이트 실패", e); + throw new RuntimeException(e); + } + } + } \ No newline at end of file diff --git a/src/main/java/model/Article.java b/src/main/java/model/Article.java index 7d02b6d9e..309b542c1 100644 --- a/src/main/java/model/Article.java +++ b/src/main/java/model/Article.java @@ -6,25 +6,31 @@ public class Article { private String writerName; private String title; private String content; + private String imageUrl; + private Long likeCount; - public Article(Long id, Long creatorId, String title, String content) { + public Article(Long id, Long creatorId, String title, String content, String imageUrl, Long likeCount) { this.id = id; this.creatorId = creatorId; this.title = title; this.content = content; + this.imageUrl = imageUrl; + this.likeCount = likeCount; } - public Article(Long creatorId, String title, String content) { + public Article(Long creatorId, String title, String content, String imageUrl) { this.creatorId = creatorId; this.title = title; this.content = content; + this.imageUrl = imageUrl; + this.likeCount = 0L; } public Long getId() { return id; } - public long getCreatorId() { + public Long getCreatorId() { return creatorId; } @@ -40,6 +46,14 @@ public String getContent() { return content; } + public String getImageUrl() { + return imageUrl; + } + + public Long getLikeCount() { + return likeCount; + } + public void setId(Long id) { this.id = id; } @@ -47,4 +61,8 @@ public void setId(Long id) { public void setWriterName(String creatorName) { this.writerName = creatorName; } + + public void increaseLikeCount() { + likeCount++; + } } diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index c4593aa3c..ef672fdce 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -7,12 +7,14 @@ public class User { private String password; private String name; private String email; + private String imageUrl; - public User(Long id, String password, String name, String email) { + public User(Long id, String password, String name, String email, String imageUrl) { this.id = id; this.password = password; this.name = name; this.email = email; + this.imageUrl = imageUrl; } public User(String password, String name, String email) { @@ -51,4 +53,20 @@ public void setId(Long id) { public String toString() { return "User [userId=" + id + ", password=" + password + ", name=" + name + ", email=" + email + "]"; } + + public void changeProfileImage(String imageUrl) { + this.imageUrl = imageUrl; + } + + public String getImageUrl() { + return imageUrl; + } + + public void changeName(String name) { + this.name = name; + } + + public void changePassword(String password) { + this.password = password; + } } diff --git a/src/main/java/webserver/handler/CreateArticleFormHandler.java b/src/main/java/webserver/handler/CreateArticleFormHandler.java index 4fe7c00ed..f6661ad1c 100644 --- a/src/main/java/webserver/handler/CreateArticleFormHandler.java +++ b/src/main/java/webserver/handler/CreateArticleFormHandler.java @@ -4,8 +4,8 @@ import webserver.http.Response; import webserver.mvc.Handler; import webserver.mvc.ModelAndView; -import webserver.mvc.RedirectView; -import webserver.mvc.StaticResourceView; +import webserver.view.RedirectView; +import webserver.view.StaticResourceView; import webserver.util.AuthUtil; public class CreateArticleFormHandler implements Handler { diff --git a/src/main/java/webserver/handler/CreateArticleHandler.java b/src/main/java/webserver/handler/CreateArticleHandler.java index a51cf9f96..d1c5cb9c4 100644 --- a/src/main/java/webserver/handler/CreateArticleHandler.java +++ b/src/main/java/webserver/handler/CreateArticleHandler.java @@ -3,14 +3,21 @@ import db.ArticleRepository; import model.Article; import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import webserver.http.Request; import webserver.http.Response; -import webserver.mvc.Handler; -import webserver.mvc.ModelAndView; -import webserver.mvc.RedirectView; +import webserver.mvc.*; import webserver.util.AuthUtil; +import webserver.mvc.ModelAndView; +import webserver.util.MultipartFileUtil; +import webserver.view.RedirectView; +import webserver.view.StaticResourceView; + +import java.io.IOException; public class CreateArticleHandler implements Handler { + private static final Logger logger = LoggerFactory.getLogger(CreateArticleHandler.class); private final ArticleRepository articleRepository; public CreateArticleHandler(ArticleRepository articleRepository) { @@ -26,7 +33,21 @@ public ModelAndView handle(Request request, Response response) { String title = request.getParameter("title"); String content = request.getParameter("content"); - articleRepository.save(new Article(user.getId(), title, content)); + if(request.getMultipartFiles().size() != 1){ + logger.debug("글 생성 요청에 이미지 없음"); + return new StaticResourceView("/article/error.html"); + } + + MultipartFile multipartFile = request.getMultipartFiles().get(0); + + String imageUrl = null; + try { + imageUrl = MultipartFileUtil.saveFile("uploads/images", multipartFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + + articleRepository.save(new Article(user.getId(), title, content, imageUrl)); return new RedirectView("/"); } diff --git a/src/main/java/webserver/handler/CreateUserHandler.java b/src/main/java/webserver/handler/CreateUserHandler.java index 7278c57cb..6625a228c 100644 --- a/src/main/java/webserver/handler/CreateUserHandler.java +++ b/src/main/java/webserver/handler/CreateUserHandler.java @@ -4,12 +4,14 @@ import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import webserver.exception.BusinessException; import webserver.http.Request; import webserver.http.Response; import webserver.mvc.Handler; import webserver.mvc.ModelAndView; -import webserver.mvc.RedirectView; +import webserver.view.FormErrorDynamicView; +import webserver.view.RedirectView; + +import java.util.Map; public class CreateUserHandler implements Handler { private static final Logger logger = LoggerFactory.getLogger(CreateUserHandler.class); @@ -28,10 +30,18 @@ public ModelAndView handle(Request request, Response response) { logger.debug("password = {} name = {} email = {}", password, name, email); if(userRepository.findByEmail(email).isPresent()){ - throw new BusinessException(); + return new FormErrorDynamicView("/registration/error.html", Map.of("error", "중복된 이메일이 존재합니다")); + } + + if(password == null || name == null || email == null){ + return new FormErrorDynamicView("/registration/error.html", Map.of("error", "비밀번호, 닉네임, 이메일은 4자리 이상이어야 합니다")); + } + + if(password.length() < 4 || name.length() < 4 || email.length() < 4 ){ + return new FormErrorDynamicView("/registration/error.html", Map.of("error", "비밀번호, 닉네임, 이메일은 4자리 이상이어야 합니다")); } - User user = new User(password, name, email); + User user = new User(password.trim(), name.trim(), email.trim()); userRepository.save(user); diff --git a/src/main/java/webserver/handler/DeleteProfileImageHandler.java b/src/main/java/webserver/handler/DeleteProfileImageHandler.java new file mode 100644 index 000000000..5648c6637 --- /dev/null +++ b/src/main/java/webserver/handler/DeleteProfileImageHandler.java @@ -0,0 +1,42 @@ +package webserver.handler; + +import db.UserRepository; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webserver.exception.BusinessException; +import webserver.http.Request; +import webserver.http.Response; +import webserver.mvc.Handler; +import webserver.mvc.ModelAndView; +import webserver.util.AuthUtil; +import webserver.view.RedirectView; + +public class DeleteProfileImageHandler implements Handler { + private static final Logger logger = LoggerFactory.getLogger(DeleteProfileImageHandler.class); + private final UserRepository userRepository; + + public DeleteProfileImageHandler(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public ModelAndView handle(Request request, Response response) { + if (!AuthUtil.isAuthenticatedUser(request)) { + return new RedirectView("/login"); + } + + User cached = AuthUtil.getAuthenticatedUser(request); + + User user = userRepository.findByEmail(cached.getEmail()) + .orElseThrow(() -> new BusinessException()); + + user.changeProfileImage(null); + + userRepository.update(user); + + logger.debug("프로필 이미지 삭제"); + + return new RedirectView("/mypage"); + } +} diff --git a/src/main/java/webserver/handler/GetImageHandler.java b/src/main/java/webserver/handler/GetImageHandler.java new file mode 100644 index 000000000..34754914d --- /dev/null +++ b/src/main/java/webserver/handler/GetImageHandler.java @@ -0,0 +1,19 @@ +package webserver.handler; + +import webserver.exception.StaticResourceNotFoundException; +import webserver.http.Request; +import webserver.http.Response; +import webserver.mvc.Handler; +import webserver.view.UploadedFileView; +import webserver.mvc.ModelAndView; + +public class GetImageHandler implements Handler { + @Override + public ModelAndView handle(Request request, Response response) { + String imageFileUrl = request.getParameter("imageUrl"); + if(imageFileUrl == null){ + throw new StaticResourceNotFoundException(); + } + return new UploadedFileView("/uploads/images/" + imageFileUrl); + } +} diff --git a/src/main/java/webserver/handler/IncreaseArticleLikeHandler.java b/src/main/java/webserver/handler/IncreaseArticleLikeHandler.java new file mode 100644 index 000000000..f44170256 --- /dev/null +++ b/src/main/java/webserver/handler/IncreaseArticleLikeHandler.java @@ -0,0 +1,34 @@ +package webserver.handler; + +import db.ArticleRepository; +import model.Article; +import webserver.exception.BusinessException; +import webserver.http.Request; +import webserver.http.Response; +import webserver.mvc.Handler; +import webserver.mvc.ModelAndView; +import webserver.view.RedirectView; + +public class IncreaseArticleLikeHandler implements Handler { + private final ArticleRepository articleRepository; + + public IncreaseArticleLikeHandler(ArticleRepository articleRepository) { + this.articleRepository = articleRepository; + } + + @Override + public ModelAndView handle(Request request, Response response) { + String articleId = request.getParameter("articleId"); + + long id = Long.parseLong(articleId); + + Article article = articleRepository.findById(id) + .orElseThrow(() -> new BusinessException()); + + article.increaseLikeCount(); + + articleRepository.update(article); + + return new RedirectView("/?articleId=" + articleId); + } +} diff --git a/src/main/java/webserver/handler/LoginFormHandler.java b/src/main/java/webserver/handler/LoginFormHandler.java index caa11a7cb..86a2c2fd8 100644 --- a/src/main/java/webserver/handler/LoginFormHandler.java +++ b/src/main/java/webserver/handler/LoginFormHandler.java @@ -6,7 +6,7 @@ import webserver.http.Response; import webserver.mvc.Handler; import webserver.mvc.ModelAndView; -import webserver.mvc.StaticResourceView; +import webserver.view.StaticResourceView; public class LoginFormHandler implements Handler { private static final Logger logger = LoggerFactory.getLogger(LoginFormHandler.class); diff --git a/src/main/java/webserver/handler/LoginHandler.java b/src/main/java/webserver/handler/LoginHandler.java index bc09b911e..8707a43db 100644 --- a/src/main/java/webserver/handler/LoginHandler.java +++ b/src/main/java/webserver/handler/LoginHandler.java @@ -1,6 +1,5 @@ package webserver.handler; -import db.Database; import db.UserRepository; import model.User; import org.slf4j.Logger; @@ -8,8 +7,8 @@ import webserver.http.Request; import webserver.http.Response; import webserver.mvc.Handler; -import webserver.mvc.RedirectView; -import webserver.mvc.StaticResourceView; +import webserver.view.RedirectView; +import webserver.view.StaticResourceView; import webserver.session.SessionStore; import webserver.mvc.ModelAndView; @@ -29,18 +28,21 @@ public ModelAndView handle(Request request, Response response) { String email = request.getParameter("email"); String password = request.getParameter("password"); - Optional byEmail = userRepository.findByEmail(email); - if(byEmail.isEmpty()){ - return new StaticResourceView("/login/error.html"); + Optional byEmail = userRepository.findByEmail(email.trim()); + if( byEmail.isEmpty()){ + logger.debug("유저 없음"); + return new StaticResourceView("/login/error_due_to_id.html"); } - if(!matchedPassword(byEmail.get(), password)){ - return new StaticResourceView("/login/error.html"); + User user = byEmail.get(); + if(password == null || !matchedPassword(user.getPassword(), password.trim())){ + logger.debug("비번 일치 안함"); + return new StaticResourceView("/login/error_due_to_pw.html"); } SessionStore sessionStore = request.getSessionStore(); String sessionId = UUID.randomUUID().toString(); - sessionStore.addSession(sessionId, byEmail.get()); + sessionStore.addSession(sessionId, user); logger.debug("session created = {}", sessionId); @@ -50,7 +52,7 @@ public ModelAndView handle(Request request, Response response) { return new RedirectView("/"); } - private static boolean matchedPassword(User userById, String password) { - return userById.getPassword().equals(password); + private static boolean matchedPassword(String originalPassword, String password) { + return originalPassword.equals(password); } } diff --git a/src/main/java/webserver/handler/LogoutHandler.java b/src/main/java/webserver/handler/LogoutHandler.java index b1bb19a96..8673ecc79 100644 --- a/src/main/java/webserver/handler/LogoutHandler.java +++ b/src/main/java/webserver/handler/LogoutHandler.java @@ -5,7 +5,7 @@ import webserver.http.Request; import webserver.http.Response; import webserver.mvc.Handler; -import webserver.mvc.RedirectView; +import webserver.view.RedirectView; import webserver.util.AuthUtil; import webserver.session.SessionStore; import webserver.mvc.ModelAndView; diff --git a/src/main/java/webserver/handler/MainHandler.java b/src/main/java/webserver/handler/MainHandler.java index 8c3bf361d..ccd597595 100644 --- a/src/main/java/webserver/handler/MainHandler.java +++ b/src/main/java/webserver/handler/MainHandler.java @@ -2,17 +2,18 @@ import db.ArticleRepository; import db.UserRepository; -import model.Article; import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import webserver.exception.BusinessException; import webserver.http.Request; import webserver.http.Response; import webserver.mvc.*; import webserver.util.AuthUtil; +import webserver.view.MainPageDynamicView; +import webserver.mvc.ModelAndView; import java.util.HashMap; -import java.util.List; import java.util.Map; public class MainHandler implements Handler { @@ -29,21 +30,89 @@ public ModelAndView handle(Request request, Response response) { Map model = new HashMap<>(); User loginUser = AuthUtil.getAuthenticatedUser(request); - if(loginUser != null){ + if (loginUser != null) { logger.debug("session found"); - model.put("name", loginUser.getName()); + + User user = userRepository.findById(loginUser.getId()) + .orElseThrow(() -> new BusinessException()); + model.put("name", user.getName()); + } + + if (hasArticleId(request)) { + try{ + long articleId = getArticleId(request); + findOne(articleId, model); + } + catch(NumberFormatException e){ + findLatest(model); + } + } else { + findLatest(model); } - List
latests = articleRepository.findTopNLessThanByIdDecreasingOrder(1, 100L); - if(!latests.isEmpty()){ - logger.debug("found latest article"); - Article article = latests.get(0); + return new MainPageDynamicView(model, "/index_logined.html"); + } + + private void findOne(long articleId, Map model) { + articleRepository.findById(articleId).ifPresent(article -> { + logger.debug("found certain article"); + User user = userRepository.findById(article.getCreatorId()) - .orElseThrow(() -> new RuntimeException()); + .orElseThrow(() -> new BusinessException()); + article.setWriterName(user.getName()); + model.put("article", article); - } + if (user.getImageUrl() != null) { + model.put("writer_profile_image", user.getImageUrl()); + } + + articleRepository.findNext(article.getId()).ifPresent(next -> { + model.put("next_article", next); + }); + + articleRepository.findPrev(article.getId()).ifPresent(prev -> { + model.put("prev_article", prev); + }); + }); + } + + private void findLatest(Map model) { + articleRepository.findLatest().ifPresentOrElse(article -> { + logger.debug("found latest article"); + + User user = userRepository.findById(article.getCreatorId()) + .orElseThrow(() -> new BusinessException()); + + article.setWriterName(user.getName()); + + model.put("article", article); + + if (user.getImageUrl() != null) { + model.put("writer_profile_image", user.getImageUrl()); + } + + articleRepository.findNext(article.getId()).ifPresent(next -> { + logger.debug("added next"); + model.put("next_article", next); + }); + + articleRepository.findPrev(article.getId()).ifPresent(prev -> { + logger.debug("added prev"); + model.put("prev_article", prev); + }); + }, + () -> { + model.put("message", "첫번째 게시글을 작성해주세요"); + }); + } + + private boolean hasArticleId(Request request) { + return request.getParameter("articleId") != null; + } - return new MainPageDynamicView(model,"/index_logined.html"); + private long getArticleId(Request request) { + String articleId = request.getParameter("articleId"); + return Integer.parseInt(articleId); } } diff --git a/src/main/java/webserver/handler/MyPageHandler.java b/src/main/java/webserver/handler/MyPageHandler.java index 2ebd88024..a33ee8bf7 100644 --- a/src/main/java/webserver/handler/MyPageHandler.java +++ b/src/main/java/webserver/handler/MyPageHandler.java @@ -1,19 +1,42 @@ package webserver.handler; +import db.UserRepository; +import model.User; +import webserver.exception.BusinessException; import webserver.http.Request; import webserver.http.Response; -import webserver.mvc.Handler; -import webserver.mvc.RedirectView; -import webserver.mvc.StaticResourceView; -import webserver.util.AuthUtil; +import webserver.mvc.*; import webserver.mvc.ModelAndView; +import webserver.view.MyPageDynamicView; +import webserver.util.AuthUtil; +import webserver.view.RedirectView; + +import java.util.HashMap; public class MyPageHandler implements Handler { + private final UserRepository userRepository; + + public MyPageHandler(UserRepository userRepository) { + this.userRepository = userRepository; + } + @Override public ModelAndView handle(Request request, Response response) { if(!AuthUtil.isAuthenticatedUser(request)){ return new RedirectView("/login"); } - return new StaticResourceView("/mypage/index.html"); + + User cachedUser = AuthUtil.getAuthenticatedUser(request); + + User user = userRepository.findByEmail(cachedUser.getEmail()) + .orElseThrow(() -> new BusinessException()); + + HashMap model = new HashMap<>(); + model.put("name", user.getName()); + String imageUrl = user.getImageUrl(); + if(imageUrl != null){ + model.put("profile_image", imageUrl); + } + return new MyPageDynamicView(model, "/mypage/index.html"); } } diff --git a/src/main/java/webserver/handler/PatchMyPageHandler.java b/src/main/java/webserver/handler/PatchMyPageHandler.java new file mode 100644 index 000000000..c2273907a --- /dev/null +++ b/src/main/java/webserver/handler/PatchMyPageHandler.java @@ -0,0 +1,67 @@ +package webserver.handler; + +import db.UserRepository; +import model.User; +import webserver.exception.BusinessException; +import webserver.http.Request; +import webserver.http.Response; +import webserver.mvc.Handler; +import webserver.mvc.ModelAndView; +import webserver.mvc.MultipartFile; +import webserver.util.AuthUtil; +import webserver.util.MultipartFileUtil; +import webserver.view.RedirectView; + +import java.io.IOException; + +public class PatchMyPageHandler implements Handler { + private final UserRepository userRepository; + + public PatchMyPageHandler(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public ModelAndView handle(Request request, Response response) { + if (!AuthUtil.isAuthenticatedUser(request)) { + return new RedirectView("/login"); + } + + User cachedUser = AuthUtil.getAuthenticatedUser(request); + User userByEmail = userRepository.findByEmail(cachedUser.getEmail()) + .orElseThrow(() -> new BusinessException()); + + boolean changed = false; + if(request.getMultipartFiles().size() == 1){ + MultipartFile multipartFile = request.getMultipartFiles().get(0); + + String imageUrl = null; + try { + imageUrl = MultipartFileUtil.saveFile("uploads/images", multipartFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + + userByEmail.changeProfileImage(imageUrl); + changed = true; + } + + String name = request.getParameter("name"); + if(name != null && name.length() >= 4 && !name.isBlank()){ + userByEmail.changeName(name.trim()); + changed = true; + } + + String password = request.getParameter("password"); + if(password != null && password.length() >= 4 && !password.isBlank()){ + userByEmail.changePassword(password.trim()); + changed = true; + } + + if(changed){ + userRepository.update(userByEmail); + } + + return new RedirectView("/mypage"); + } +} diff --git a/src/main/java/webserver/handler/RegisterFormHandler.java b/src/main/java/webserver/handler/RegisterFormHandler.java index 985ea3032..b00b07d63 100644 --- a/src/main/java/webserver/handler/RegisterFormHandler.java +++ b/src/main/java/webserver/handler/RegisterFormHandler.java @@ -6,7 +6,7 @@ import webserver.http.Response; import webserver.mvc.Handler; import webserver.mvc.ModelAndView; -import webserver.mvc.StaticResourceView; +import webserver.view.StaticResourceView; public class RegisterFormHandler implements Handler { private static final Logger logger = LoggerFactory.getLogger(RegisterFormHandler.class); diff --git a/src/main/java/webserver/handler/StaticResourceHandler.java b/src/main/java/webserver/handler/StaticResourceHandler.java index 98405b20d..11640a10d 100644 --- a/src/main/java/webserver/handler/StaticResourceHandler.java +++ b/src/main/java/webserver/handler/StaticResourceHandler.java @@ -5,7 +5,7 @@ import webserver.http.Request; import webserver.http.Response; import webserver.mvc.Handler; -import webserver.mvc.StaticResourceView; +import webserver.view.StaticResourceView; import webserver.mvc.ModelAndView; public class StaticResourceHandler implements Handler { diff --git a/src/main/java/webserver/http/Request.java b/src/main/java/webserver/http/Request.java index d5ee80a2f..419df10f4 100644 --- a/src/main/java/webserver/http/Request.java +++ b/src/main/java/webserver/http/Request.java @@ -73,6 +73,10 @@ public List getMultipartFiles() { return multipartFiles; } + public void setParameter(String name, String value) { + params.put(name, value); + } + @Override public String toString() { diff --git a/src/main/java/webserver/mvc/PathVariableRouting.java b/src/main/java/webserver/mvc/PathVariableRouting.java new file mode 100644 index 000000000..db79f4e70 --- /dev/null +++ b/src/main/java/webserver/mvc/PathVariableRouting.java @@ -0,0 +1,58 @@ +package webserver.mvc; + +import webserver.http.Request; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class PathVariableRouting implements Routing { + private Map handlerMap; + private List pathVariableNames = new ArrayList<>(); + private Pattern pattern; + + public PathVariableRouting(String uri, Map handlerMap) { + this.handlerMap = handlerMap; + String regex = Arrays.stream(uri.split("/")) + .map(token -> { + if (token.startsWith("{") && token.endsWith("}")) { + String varName = token.substring(1, token.length() - 1); + pathVariableNames.add(varName); + return "([^/]+)"; + } + return token; + }) + .collect(Collectors.joining("/")); + + this.pattern = Pattern.compile(regex); + } + + @Override + public boolean supportsUri(String uri) { + return pattern.matcher(uri).matches(); + } + + @Override + public boolean supportsMethod(String method) { + return handlerMap.containsKey(method); + } + + @Override + public Handler resolveHandler(Request request) { + Matcher matcher = pattern.matcher(request.getPath()); + + if (matcher.matches()) { + for (int i = 0; i < pathVariableNames.size(); i++) { + String name = pathVariableNames.get(i); + String value = matcher.group(i + 1); // group(1)부터 시작 + request.setParameter(name, value); + } + } + + return handlerMap.get(request.getMethod()); + } +} diff --git a/src/main/java/webserver/mvc/RequestHandler.java b/src/main/java/webserver/mvc/RequestHandler.java index f78917444..f92c374d6 100644 --- a/src/main/java/webserver/mvc/RequestHandler.java +++ b/src/main/java/webserver/mvc/RequestHandler.java @@ -2,7 +2,8 @@ import java.io.*; import java.net.Socket; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import db.ArticleRepository; @@ -11,17 +12,18 @@ import db.UserRepositoryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import webserver.exception.BadRequestException; import webserver.exception.MethodNotAllowedException; -import webserver.exception.RequestParsingException; import webserver.exception.StaticResourceNotFoundException; import webserver.handler.*; import webserver.http.Request; import webserver.http.Response; import webserver.session.SessionStore; +import webserver.view.StaticResourceView; public class RequestHandler implements Runnable { private static final Logger logger = LoggerFactory.getLogger(RequestHandler.class); - private static Map> routingTable; + private static List routingTable; private static Handler staticResourceHandler; private static SessionStore sessionStore; private Socket connection; @@ -29,15 +31,20 @@ public class RequestHandler implements Runnable { static{ UserRepository userRepository = new UserRepositoryImpl(); ArticleRepository articleRepository = new ArticleRepositoryImpl(); - routingTable = new HashMap<>(); - routingTable.put("/", Map.of("GET", new MainHandler(articleRepository, userRepository))); - routingTable.put("/registration", Map.of("GET", new RegisterFormHandler())); - routingTable.put("/create", Map.of("POST", new CreateUserHandler(userRepository))); - routingTable.put("/login", Map.of("GET", new LoginFormHandler(), "POST", new LoginHandler(userRepository))); - routingTable.put("/logout", Map.of("POST", new LogoutHandler())); - routingTable.put("/mypage", Map.of("GET", new MyPageHandler())); - routingTable.put("/article/create-form", Map.of("GET", new CreateArticleFormHandler())); - routingTable.put("/article", Map.of("POST", new CreateArticleHandler(articleRepository))); + + routingTable = new ArrayList<>(); + routingTable.add(new SimpleRouting("/", Map.of("GET", new MainHandler(articleRepository, userRepository)))); + routingTable.add(new SimpleRouting("/registration", Map.of("GET", new RegisterFormHandler()))); + routingTable.add(new SimpleRouting("/create", Map.of("POST", new CreateUserHandler(userRepository)))); + routingTable.add(new SimpleRouting("/login", Map.of("GET", new LoginFormHandler(), "POST", new LoginHandler(userRepository)))); + routingTable.add(new SimpleRouting("/logout", Map.of("POST", new LogoutHandler()))); + routingTable.add(new SimpleRouting("/mypage", Map.of("GET", new MyPageHandler(userRepository), "PATCH", new PatchMyPageHandler(userRepository)))); + routingTable.add(new SimpleRouting("/article/create-form", Map.of("GET", new CreateArticleFormHandler()))); + routingTable.add(new SimpleRouting("/article", Map.of("POST", new CreateArticleHandler(articleRepository)))); + routingTable.add(new PathVariableRouting("/uploads/images/{imageUrl}", Map.of("GET", new GetImageHandler()))); + routingTable.add(new SimpleRouting("/mypage/profile-image", Map.of("DELETE", new DeleteProfileImageHandler(userRepository)))); + routingTable.add(new PathVariableRouting("/article/{articleId}/like", Map.of("POST", new IncreaseArticleLikeHandler(articleRepository)))); + staticResourceHandler = new StaticResourceHandler(); sessionStore = new SessionStore(); } @@ -71,7 +78,7 @@ private void processRequest(OutputStream out, InputStream in) throws IOException logger.debug("request parsing complete"); logger.debug("request={}", request); - Handler handler = resovleHandler(request.getPath(), request.getMethod()); + Handler handler = resovleHandler(request); ModelAndView mav = handler.handle(request, response); mav.render(response); @@ -88,9 +95,10 @@ private void processRequest(OutputStream out, InputStream in) throws IOException logger.debug("method not allowed error", e); sendErrorPage(responseWriter, Response.methodNotAllowed(), new StaticResourceView("/error/405_error.html")); } - catch (RequestParsingException e){ + catch (BadRequestException e){ logger.debug("request parsing error", e); - responseWriter.write(Response.badRequest()); + sendErrorPage(responseWriter, Response.badRequest(), new StaticResourceView("/error/400_error.html")); + } catch (RuntimeException e){ logger.debug("internal server error", e); @@ -103,14 +111,14 @@ private static void sendErrorPage(ResponseWriter responseWriter, Response respon responseWriter.write(response); } - private Handler resovleHandler(String uri, String method) { - Map methodHandlers = routingTable.get(uri); - if(methodHandlers != null){ - Handler handler = methodHandlers.get(method); - if(handler == null){ - throw new MethodNotAllowedException(); + private Handler resovleHandler(Request request) { + for(Routing routing : routingTable){ + if(routing.supportsUri(request.getPath())){ + if(!routing.supportsMethod(request.getMethod())){ + throw new MethodNotAllowedException(); + } + return routing.resolveHandler(request); } - return handler; } return staticResourceHandler; } diff --git a/src/main/java/webserver/mvc/RequestParser.java b/src/main/java/webserver/mvc/RequestParser.java index 7d2a6fa25..be3b8e0f7 100644 --- a/src/main/java/webserver/mvc/RequestParser.java +++ b/src/main/java/webserver/mvc/RequestParser.java @@ -5,6 +5,7 @@ import webserver.exception.RequestParsingException; import java.io.*; +import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.*; @@ -86,7 +87,7 @@ private void parseRequestLineAndHeaders(byte[] headerBytes) { private void parsePathAndQuery(String url) { String[] tokens = url.split("\\?", 2); - path = tokens[0]; + path = URLDecoder.decode(tokens[0], StandardCharsets.UTF_8);; if (tokens.length == 2) { parseQueryString(tokens[1]); diff --git a/src/main/java/webserver/mvc/Routing.java b/src/main/java/webserver/mvc/Routing.java new file mode 100644 index 000000000..cc8bff211 --- /dev/null +++ b/src/main/java/webserver/mvc/Routing.java @@ -0,0 +1,11 @@ +package webserver.mvc; + +import webserver.http.Request; + +public interface Routing { + boolean supportsUri(String uri); + + boolean supportsMethod(String method); + + Handler resolveHandler(Request method); +} diff --git a/src/main/java/webserver/mvc/SimpleRouting.java b/src/main/java/webserver/mvc/SimpleRouting.java new file mode 100644 index 000000000..00ecdec61 --- /dev/null +++ b/src/main/java/webserver/mvc/SimpleRouting.java @@ -0,0 +1,30 @@ +package webserver.mvc; + +import webserver.http.Request; + +import java.util.Map; + +public class SimpleRouting implements Routing { + private String uri; + private Map handlerMap; + + public SimpleRouting(String uri, Map handlerMap) { + this.uri = uri; + this.handlerMap = handlerMap; + } + + @Override + public boolean supportsUri(String uri) { + return this.uri.equals(uri); + } + + @Override + public boolean supportsMethod(String method) { + return this.handlerMap.containsKey(method); + } + + @Override + public Handler resolveHandler(Request request) { + return handlerMap.get(request.getMethod()); + } +} diff --git a/src/main/java/webserver/util/MultipartFileUtil.java b/src/main/java/webserver/util/MultipartFileUtil.java new file mode 100644 index 000000000..2ff9e736d --- /dev/null +++ b/src/main/java/webserver/util/MultipartFileUtil.java @@ -0,0 +1,39 @@ +package webserver.util; + +import webserver.mvc.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.UUID; + +public class MultipartFileUtil { + public static String saveFile(String directoryPath, MultipartFile multipartFile) throws IOException { + if (directoryPath == null || directoryPath.isBlank()) { + throw new IllegalArgumentException(); + } + + String filename = UUID.randomUUID() + multipartFile.getFilename(); + + Path uploadDir = Paths.get(System.getProperty("user.dir")) + .resolve(directoryPath); + + Files.createDirectories(uploadDir); + + Path target = uploadDir.resolve(filename); + + try (InputStream in = multipartFile.getInputStream(); + OutputStream out = Files.newOutputStream(target, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING)) { + + in.transferTo(out); + } + + return directoryPath + "/" + filename; + } +} diff --git a/src/main/java/webserver/view/FormErrorDynamicView.java b/src/main/java/webserver/view/FormErrorDynamicView.java new file mode 100644 index 000000000..862520000 --- /dev/null +++ b/src/main/java/webserver/view/FormErrorDynamicView.java @@ -0,0 +1,54 @@ +package webserver.view; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webserver.constant.FileMimeType; +import webserver.exception.StaticResourceNotFoundException; +import webserver.http.Response; +import webserver.mvc.ModelAndView; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +public class FormErrorDynamicView implements ModelAndView { + private static final Logger logger = LoggerFactory.getLogger(FormErrorDynamicView.class); + private String viewName; + private Map model; + + public FormErrorDynamicView(String viewName, Map model) { + this.viewName = viewName; + this.model = model; + } + + @Override + public String getViewName() { + return viewName; + } + + @Override + public void render(Response response) { + try { + Path filePath = Paths.get("./src/main/resources/static" + viewName); + String baseHtml = Files.readString(filePath, StandardCharsets.UTF_8); + + logger.debug("before baseHtml = {} ", baseHtml); + + if (model.containsKey("error")) { + baseHtml = baseHtml.replace("${{error}}", (String) model.get("error")); + } + + logger.debug("after baseHtml = {} ", baseHtml); + + byte[] body = baseHtml.getBytes(StandardCharsets.UTF_8); + String contentType = FileMimeType.resolveMimeType(getViewName()); + response.setOk(body, contentType); + } catch (IOException e) { + logger.error("error occurred while reading static resource"); + throw new StaticResourceNotFoundException(); + } + } +} diff --git a/src/main/java/webserver/mvc/MainPageDynamicView.java b/src/main/java/webserver/view/MainPageDynamicView.java similarity index 57% rename from src/main/java/webserver/mvc/MainPageDynamicView.java rename to src/main/java/webserver/view/MainPageDynamicView.java index f67929baa..d18218421 100644 --- a/src/main/java/webserver/mvc/MainPageDynamicView.java +++ b/src/main/java/webserver/view/MainPageDynamicView.java @@ -1,4 +1,4 @@ -package webserver.mvc; +package webserver.view; import model.Article; import org.slf4j.Logger; @@ -6,6 +6,7 @@ import webserver.constant.FileMimeType; import webserver.exception.StaticResourceNotFoundException; import webserver.http.Response; +import webserver.mvc.ModelAndView; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -14,7 +15,7 @@ import java.nio.file.Paths; import java.util.Map; -public class MainPageDynamicView implements ModelAndView{ +public class MainPageDynamicView implements ModelAndView { private static final Logger logger = LoggerFactory.getLogger(MainPageDynamicView.class); private Map model; private String viewName; @@ -31,54 +32,80 @@ public String getViewName() { @Override public void render(Response response) { - try{ + try { Path filePath = Paths.get("./src/main/resources/static" + getViewName()); String baseHtml = Files.readString(filePath, StandardCharsets.UTF_8); - logger.debug("before baseHtml = {} " , baseHtml); + logger.debug("before baseHtml = {} ", baseHtml); - if(model.containsKey("name")){ + if (model.containsKey("name")) { Path templatePath = Paths.get("./src/main/resources/static" + "/template/header_menu_logined.html"); String headerMenuTemplate = Files.readString(templatePath, StandardCharsets.UTF_8); logger.debug("headerMenuTemplate= {}", headerMenuTemplate); headerMenuTemplate = headerMenuTemplate.replace("${{name}}", (String) model.get("name")); baseHtml = baseHtml.replace("${{header_menu}}", headerMenuTemplate); - } - else{ + } else { Path templatePath = Paths.get("./src/main/resources/static" + "/template/header_menu_public.html"); String headerMenuTemplate = Files.readString(templatePath, StandardCharsets.UTF_8); logger.debug("headerMenuTemplate= {}", headerMenuTemplate); baseHtml = baseHtml.replace("${{header_menu}}", headerMenuTemplate); } - if(model.containsKey("article")){ + if (model.containsKey("article")) { Article article = (Article) model.get("article"); Path templatePath = Paths.get("./src/main/resources/static" + "/template/article.html"); String articleTemplate = Files.readString(templatePath, StandardCharsets.UTF_8); logger.debug("articleTemplate = {}", articleTemplate); + articleTemplate = articleTemplate.replace("${{article_id}}", String.valueOf(article.getId())); articleTemplate = articleTemplate.replace("${{writer}}", article.getWriterName()); articleTemplate = articleTemplate.replace("${{content}}", article.getContent()); + articleTemplate = articleTemplate.replace("${{image_url}}", article.getImageUrl()); + articleTemplate = articleTemplate.replace("${{article_like_count}}", String.valueOf(article.getLikeCount())); + + if (model.containsKey("writer_profile_image")) { + articleTemplate = articleTemplate.replace("${{writer_profile_image}}", (String) model.get("writer_profile_image")); + } else { + articleTemplate = articleTemplate.replace("${{writer_profile_image}}", ""); + } - if(!model.containsKey("comments")){ + if (!model.containsKey("comments")) { articleTemplate = articleTemplate.replace("${{comments}}", "댓글이 없습니다"); articleTemplate = articleTemplate.replace("${{all-comments-button}}", ""); } baseHtml = baseHtml.replace("${{article}}", articleTemplate); + templatePath = Paths.get("./src/main/resources/static" + "/template/article_add_comment_button.html"); + String addCommentButtonTemplate = Files.readString(templatePath, StandardCharsets.UTF_8); + + baseHtml = baseHtml.replace("${{article_add_comment_button}}", addCommentButtonTemplate); + + } else { + baseHtml = baseHtml.replace("${{article}}", (String) model.getOrDefault("message", "존재하지 않는 게시물입니다.")); + baseHtml = baseHtml.replace("${{article_add_comment_button}}", ""); } - else{ - baseHtml = baseHtml.replace("${{article}}", "첫 게시글을 써주세요"); + + if (model.containsKey("next_article")) { + Article next = (Article) model.get("next_article"); + baseHtml = baseHtml.replace("${{next_url}}", "/?articleId=" + next.getId()); + } else { + baseHtml = baseHtml.replace("${{next_url}}", ""); + } + + if (model.containsKey("prev_article")) { + Article prev = (Article) model.get("prev_article"); + baseHtml = baseHtml.replace("${{prev_url}}", "/?articleId=" + prev.getId()); + } else { + baseHtml = baseHtml.replace("${{prev_url}}", ""); } - logger.debug("after baseHtml = {} " , baseHtml); + logger.debug("after baseHtml = {} ", baseHtml); byte[] body = baseHtml.getBytes(StandardCharsets.UTF_8); String contentType = FileMimeType.resolveMimeType(getViewName()); response.setOk(body, contentType); - } - catch (IOException e) { + } catch (IOException e) { logger.error("error occurred while reading static resource"); throw new StaticResourceNotFoundException(); } diff --git a/src/main/java/webserver/view/MyPageDynamicView.java b/src/main/java/webserver/view/MyPageDynamicView.java new file mode 100644 index 000000000..b97c58071 --- /dev/null +++ b/src/main/java/webserver/view/MyPageDynamicView.java @@ -0,0 +1,61 @@ +package webserver.view; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webserver.constant.FileMimeType; +import webserver.exception.StaticResourceNotFoundException; +import webserver.http.Response; +import webserver.mvc.ModelAndView; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +public class MyPageDynamicView implements ModelAndView { + private static final Logger logger = LoggerFactory.getLogger(MyPageDynamicView.class); + private Map model; + private String viewName; + + public MyPageDynamicView(Map model, String viewName) { + this.model = model; + this.viewName = viewName; + } + + @Override + public String getViewName() { + return viewName; + } + + @Override + public void render(Response response) { + try { + Path filePath = Paths.get("./src/main/resources/static" + viewName); + String baseHtml = Files.readString(filePath, StandardCharsets.UTF_8); + + logger.debug("before baseHtml = {} ", baseHtml); + + if (model.containsKey("name")) { + baseHtml = baseHtml.replace("${{name}}", (String) model.get("name")); + } + + if (model.containsKey("profile_image")) { + baseHtml = baseHtml.replace("${{profile_image}}", (String) model.get("profile_image")); + } + else{ + baseHtml = baseHtml.replace("${{profile_image}}", ""); + } + + logger.debug("after baseHtml = {} ", baseHtml); + + byte[] body = baseHtml.getBytes(StandardCharsets.UTF_8); + String contentType = FileMimeType.resolveMimeType(getViewName()); + response.setOk(body, contentType); + } catch (IOException e) { + logger.error("error occurred while reading static resource"); + throw new StaticResourceNotFoundException(); + } + } +} diff --git a/src/main/java/webserver/mvc/RedirectView.java b/src/main/java/webserver/view/RedirectView.java similarity index 86% rename from src/main/java/webserver/mvc/RedirectView.java rename to src/main/java/webserver/view/RedirectView.java index 1fd70316e..53b9711e2 100644 --- a/src/main/java/webserver/mvc/RedirectView.java +++ b/src/main/java/webserver/view/RedirectView.java @@ -1,6 +1,7 @@ -package webserver.mvc; +package webserver.view; import webserver.http.Response; +import webserver.mvc.ModelAndView; public class RedirectView implements ModelAndView { private String viewName; diff --git a/src/main/java/webserver/mvc/StaticResourceView.java b/src/main/java/webserver/view/StaticResourceView.java similarity index 90% rename from src/main/java/webserver/mvc/StaticResourceView.java rename to src/main/java/webserver/view/StaticResourceView.java index a51b56727..43b57505c 100644 --- a/src/main/java/webserver/mvc/StaticResourceView.java +++ b/src/main/java/webserver/view/StaticResourceView.java @@ -1,16 +1,17 @@ -package webserver.mvc; +package webserver.view; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import webserver.constant.FileMimeType; import webserver.exception.StaticResourceNotFoundException; import webserver.http.Response; +import webserver.mvc.ModelAndView; import java.io.File; import java.io.IOException; import java.nio.file.Files; -public class StaticResourceView implements ModelAndView{ +public class StaticResourceView implements ModelAndView { private static final Logger logger = LoggerFactory.getLogger(StaticResourceView.class); private String viewName; diff --git a/src/main/java/webserver/view/UploadedFileView.java b/src/main/java/webserver/view/UploadedFileView.java new file mode 100644 index 000000000..a204fd635 --- /dev/null +++ b/src/main/java/webserver/view/UploadedFileView.java @@ -0,0 +1,44 @@ +package webserver.view; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webserver.constant.FileMimeType; +import webserver.exception.StaticResourceNotFoundException; +import webserver.http.Response; +import webserver.mvc.ModelAndView; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public class UploadedFileView implements ModelAndView { + private static final Logger logger = LoggerFactory.getLogger(UploadedFileView.class); + private String viewName; + + public UploadedFileView(String viewName) { + this.viewName = viewName; + } + + @Override + public String getViewName() { + return viewName; + } + + @Override + public void render(Response response) { + try{ + String projectRoot = System.getProperty("user.dir"); + String pathname = projectRoot + getViewName(); + + logger.debug("pathname : {}", pathname); + + byte[] body = Files.readAllBytes(new File(pathname).toPath()); + String contentType = FileMimeType.resolveMimeType(getViewName()); + response.setOk(body, contentType); + } + catch (IOException e) { + logger.error("error occurred while reading static resource"); + throw new StaticResourceNotFoundException(); + } + } +} diff --git a/src/main/resources/static/article/error.html b/src/main/resources/static/article/error.html new file mode 100644 index 000000000..0c9a62e2e --- /dev/null +++ b/src/main/resources/static/article/error.html @@ -0,0 +1,61 @@ + + + + + + + + + +
+
+ +
    +
  • + 글쓰기 +
  • +
  • +
    + +
    +
  • +
+
+
+

게시글 작성

+
+
+

제목

+ +
+
+

내용

+ +
+ +

이미지는 필수 항목입니다

+ +
+
+
+ + diff --git a/src/main/resources/static/article/index.html b/src/main/resources/static/article/index.html index f53ef1087..6f0b2e909 100644 --- a/src/main/resources/static/article/index.html +++ b/src/main/resources/static/article/index.html @@ -25,7 +25,7 @@

게시글 작성

-
+

제목

+
+ diff --git a/src/main/resources/static/login/error_due_to_id.html b/src/main/resources/static/login/error_due_to_id.html new file mode 100644 index 000000000..b26a8ff59 --- /dev/null +++ b/src/main/resources/static/login/error_due_to_id.html @@ -0,0 +1,66 @@ + + + + + + + + + +
+
+ + +
+
+

로그인

+ +
+

아이디

+ +
+
+

비밀번호

+ +
+

존재하지 않는 아이디입니다. 회원 가입 하시겠습니까?

+ + 회원가입 + + + +
+
+ + diff --git a/src/main/resources/static/login/error.html b/src/main/resources/static/login/error_due_to_pw.html similarity index 95% rename from src/main/resources/static/login/error.html rename to src/main/resources/static/login/error_due_to_pw.html index 21a368bf8..420b16a6a 100644 --- a/src/main/resources/static/login/error.html +++ b/src/main/resources/static/login/error_due_to_pw.html @@ -43,7 +43,7 @@

로그인

autocomplete="current-password" /> -

아이디나 비밀번호가 일치하지 않습니다.

+

비밀번호가 틀렸습니다

- - +
  • + 글쓰기 +
  • +
  • +
    + +
    +
  • - -
    + +

    마이페이지

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

    닉네임

    - -
    -
    -

    비밀번호

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

    비밀번호 확인

    - -
    - - - -
    +
    + +
    +

    닉네임

    + +
    +
    +

    비밀번호

    + +
    +
    +

    비밀번호 확인

    + +
    + +
    +
    - +
    + + diff --git a/src/main/resources/static/registration/error.html b/src/main/resources/static/registration/error.html new file mode 100644 index 000000000..61e6c4e81 --- /dev/null +++ b/src/main/resources/static/registration/error.html @@ -0,0 +1,68 @@ + + + + + + + + + +
    +
    + + +
    +
    +

    회원가입

    +
    +
    +

    아이디

    + +
    +
    +

    닉네임

    + +
    +
    +

    비밀번호

    + +
    +

    ${{error}}

    + +
    +
    +
    + + diff --git a/src/main/resources/static/template/article.html b/src/main/resources/static/template/article.html index e84fe38af..e71f50624 100644 --- a/src/main/resources/static/template/article.html +++ b/src/main/resources/static/template/article.html @@ -1,16 +1,19 @@
    - +
    • -
    • +
    • + ${{article_like_count}} +