diff --git a/build.gradle b/build.gradle index db3ebcf1..05c9215d 100644 --- a/build.gradle +++ b/build.gradle @@ -18,10 +18,19 @@ repositories { } dependencies { + // Spring Boot 핵심 implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + implementation 'org.springframework.boot:spring-boot-starter' + runtimeOnly 'com.mysql:mysql-connector-j' testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + compileOnly 'org.projectlombok:lombok:1.18.32' + annotationProcessor 'org.projectlombok:lombok:1.18.32' + } tasks.named('test') { diff --git a/src/main/java/com/example/bcsd/HelloController.java b/src/main/java/com/example/bcsd/HelloController.java deleted file mode 100644 index 9559e2f1..00000000 --- a/src/main/java/com/example/bcsd/HelloController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.example.bcsd; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -public class HelloController { - - @ResponseBody - @GetMapping("/hello") - public String hello() { - return "Hello World!!!!!"; - } - - @GetMapping("/hello2") - public String hello2() { - return "hello"; - } -} diff --git a/src/main/java/com/example/bcsd/controllers/ArticleController.java b/src/main/java/com/example/bcsd/controllers/ArticleController.java new file mode 100644 index 00000000..808d6424 --- /dev/null +++ b/src/main/java/com/example/bcsd/controllers/ArticleController.java @@ -0,0 +1,48 @@ +package com.example.bcsd.controllers; + +import com.example.bcsd.models.Article; +import com.example.bcsd.services.PostService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/articles") +public class ArticleController { + + private final PostService postService; + + public ArticleController(PostService postService) { + this.postService = postService; + } + + @GetMapping + public List
getArticlesByBoard(@RequestParam("boardId") int boardId) { + return postService.getArticlesByBoard(boardId); + } + + @GetMapping("/{id}") + public Article getArticle(@PathVariable int id) { + return postService.getArticleById(id); + } + + @PostMapping + public ResponseEntity createArticle(@RequestBody Article article) { + postService.createArticle(article); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + @PutMapping("/{id}") + public ResponseEntity updateArticle(@PathVariable int id, @RequestBody Article article) { + postService.updateArticle(id, article); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteArticle(@PathVariable int id) { + postService.deleteArticle(id); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/example/bcsd/controllers/MemberController.java b/src/main/java/com/example/bcsd/controllers/MemberController.java new file mode 100644 index 00000000..187a3736 --- /dev/null +++ b/src/main/java/com/example/bcsd/controllers/MemberController.java @@ -0,0 +1,50 @@ +package com.example.bcsd.controllers; + +import com.example.bcsd.models.Member; +import com.example.bcsd.services.MemberService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/members") +public class MemberController { + + private final MemberService memberService; + + @Autowired + public MemberController(MemberService memberService) { + this.memberService = memberService; + } + + @PostMapping + public ResponseEntity createMember(@RequestBody Member member) { + Member created = memberService.createMember(member); + return ResponseEntity.status(HttpStatus.CREATED).body(created); + } + + @GetMapping("/{id}") + public ResponseEntity getMemberById(@PathVariable int id) { + return ResponseEntity.ok(memberService.getMemberById(id)); + } + + @GetMapping + public ResponseEntity> getAllMembers() { + return ResponseEntity.ok(memberService.getAllMembers()); + } + + @PutMapping("/{id}") + public ResponseEntity updateMember(@PathVariable int id, @RequestBody Member member) { + memberService.updateMember(id, member); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteMember(@PathVariable int id) { + memberService.deleteMember(id); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/example/bcsd/controllers/PostController.java b/src/main/java/com/example/bcsd/controllers/PostController.java new file mode 100644 index 00000000..3fb4f76a --- /dev/null +++ b/src/main/java/com/example/bcsd/controllers/PostController.java @@ -0,0 +1,26 @@ +package com.example.bcsd.controllers; + +import com.example.bcsd.services.PostService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + + + +@Controller +public class PostController { + private final PostService postService; + + public PostController(PostService postService) { + this.postService = postService; + } + + @GetMapping("/posts") + public String getPosts(@RequestParam int boardId, Model model) { + model.addAttribute("boardName", postService.getBoardName(boardId)); + model.addAttribute("articles", postService.getArticlesByBoard(boardId)); + return "posts"; + } +} + diff --git a/src/main/java/com/example/bcsd/controllers/SessionController.java b/src/main/java/com/example/bcsd/controllers/SessionController.java new file mode 100644 index 00000000..9d941c2f --- /dev/null +++ b/src/main/java/com/example/bcsd/controllers/SessionController.java @@ -0,0 +1,63 @@ +package com.example.bcsd.controllers; + +import com.example.bcsd.dtos.LoginDto; +import com.example.bcsd.dtos.SignupDto; +import com.example.bcsd.models.Member; +import com.example.bcsd.repositories.MemberRepository; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@Controller +@RequiredArgsConstructor +public class SessionController { + + private final MemberRepository memberRepository; + + @GetMapping("/login") + public String loginForm() { + return "login"; + } + @GetMapping("/signup") + public String signupForm() { + return "signup"; // templates/login.html 파일 필요 + } + + @PostMapping("/signup") + public String signup(SignupDto request) { + if (!request.getPassword().equals(request.getConfirmPassword())) { + return "비밀번호가 일치하지 않습니다."; + } + + if (memberRepository.findByAccount(request.getAccount()).isPresent()) { + return "이미 존재하는 계정입니다."; + } + + Member member = new Member(); + member.setAccount(request.getAccount()); + member.setPassword(request.getPassword()); + member.setEmail(request.getEmail()); + member.setName(request.getName()); + memberRepository.save(member); + + return "redirect:/login"; + } + + @PostMapping("/login") + public String login(LoginDto request, HttpSession session) { + Optional optionalMember = memberRepository.findByAccount(request.getAccount()); + + if (optionalMember.isEmpty()) return "존재하지 않는 계정입니다."; + Member member = optionalMember.get(); + + if (!member.getPassword().equals(request.getPassword())) { + return "비밀번호가 틀렸습니다."; + } + + session.setAttribute("loginMember", member); // 세션 저장 + return "redirect:/"; + } +} diff --git a/src/main/java/com/example/bcsd/daos/ArticleDAO.java b/src/main/java/com/example/bcsd/daos/ArticleDAO.java new file mode 100644 index 00000000..0099c200 --- /dev/null +++ b/src/main/java/com/example/bcsd/daos/ArticleDAO.java @@ -0,0 +1,54 @@ +/*package com.example.bcsd.daos; + +import com.example.bcsd.models.Article; +import org.springframework.stereotype.Repository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +import java.util.List; + +@Repository +public class ArticleDAO { + + @PersistenceContext + private EntityManager em; + + public Article findById(int id) { + return em.find(Article.class, id); + } + + public List
findByBoardId(int boardId) { + return em.createQuery("SELECT a FROM Article a WHERE a.boardId = :boardId", Article.class) + .setParameter("boardId", boardId) + .getResultList(); + } + + public void insert(Article article) { + em.persist(article); + } + + public void update(int id, Article article) { + Article target = em.find(Article.class, id); + if (target != null) { + target.setBoardId(article.getBoardId()); + target.setTitle(article.getTitle()); + target.setContent(article.getContent()); + target.setUpdatedAt(java.time.LocalDateTime.now()); + } + } + + public void delete(int id) { + Article article = em.find(Article.class, id); + if (article != null) { + em.remove(article); + } + } + + public int countByAuthorId(int authorId) { + Long count = em.createQuery("SELECT COUNT(a) FROM Article a WHERE a.authorId = :authorId", Long.class) + .setParameter("authorId", authorId) + .getSingleResult(); + return count.intValue(); + } +} +*/ diff --git a/src/main/java/com/example/bcsd/daos/BoardDAO.java b/src/main/java/com/example/bcsd/daos/BoardDAO.java new file mode 100644 index 00000000..a3e6417d --- /dev/null +++ b/src/main/java/com/example/bcsd/daos/BoardDAO.java @@ -0,0 +1,30 @@ +/* +package com.example.bcsd.daos; + + +import com.example.bcsd.models.Board; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + + +@Repository +public class BoardDAO { + + @PersistenceContext + private EntityManager em; + + public Optional findNameById(int id) { + List names = em.createQuery("SELECT b.name FROM Board b WHERE b.id = :id", String.class) + .setParameter("id", id) + .getResultList(); + return names.stream().findFirst(); + } +} +*/ diff --git a/src/main/java/com/example/bcsd/daos/MemberDAO.java b/src/main/java/com/example/bcsd/daos/MemberDAO.java new file mode 100644 index 00000000..87746fe5 --- /dev/null +++ b/src/main/java/com/example/bcsd/daos/MemberDAO.java @@ -0,0 +1,51 @@ +/* +package com.example.bcsd.daos; + +import com.example.bcsd.models.Member; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public class MemberDAO { + + @PersistenceContext + private EntityManager em; + + public Member findById(int id) { + return em.find(Member.class, id); + } + + public List findAll() { + return em.createQuery("SELECT m FROM Member m", Member.class).getResultList(); + } + + public void insert(Member member) { + em.persist(member); + } + + public void update(Member member) { + Member target = em.find(Member.class, member.getId()); + if (target != null) { + target.setName(member.getName()); + target.setEmail(member.getEmail()); + } + } + + public void delete(int id) { + Member member = em.find(Member.class, id); + if (member != null) { + em.remove(member); + } + } + public Optional findByEmail(String email) { + List result = em.createQuery("SELECT m FROM Member m WHERE m.email = :email", Member.class) + .setParameter("email", email) + .getResultList(); + return result.stream().findFirst(); + } +} + */ \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/dtos/LoginDto.java b/src/main/java/com/example/bcsd/dtos/LoginDto.java new file mode 100644 index 00000000..26275b41 --- /dev/null +++ b/src/main/java/com/example/bcsd/dtos/LoginDto.java @@ -0,0 +1,10 @@ +package com.example.bcsd.dtos; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter +public class LoginDto { + private String account; + private String password; +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/dtos/SignupDto.java b/src/main/java/com/example/bcsd/dtos/SignupDto.java new file mode 100644 index 00000000..d2035ee2 --- /dev/null +++ b/src/main/java/com/example/bcsd/dtos/SignupDto.java @@ -0,0 +1,13 @@ +package com.example.bcsd.dtos; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter +public class SignupDto { + private String account; + private String password; + private String confirmPassword; + private String email; + private String name; +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/exceptions/DataConflictException.java b/src/main/java/com/example/bcsd/exceptions/DataConflictException.java new file mode 100644 index 00000000..9b526837 --- /dev/null +++ b/src/main/java/com/example/bcsd/exceptions/DataConflictException.java @@ -0,0 +1,7 @@ +package com.example.bcsd.exceptions; + +public class DataConflictException extends RuntimeException { + public DataConflictException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/exceptions/GlobalExceptionHandler.java b/src/main/java/com/example/bcsd/exceptions/GlobalExceptionHandler.java new file mode 100644 index 00000000..84942b65 --- /dev/null +++ b/src/main/java/com/example/bcsd/exceptions/GlobalExceptionHandler.java @@ -0,0 +1,29 @@ +package com.example.bcsd.exceptions; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity handleNotFound(ResourceNotFoundException ex) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); + } + + @ExceptionHandler(DataConflictException.class) + public ResponseEntity handleConflict(DataConflictException ex) { + return ResponseEntity.status(HttpStatus.CONFLICT).body(ex.getMessage()); + } + + @ExceptionHandler(InvalidRequestException.class) + public ResponseEntity handleBadRequest(InvalidRequestException ex) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleOther(Exception ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("서버 오류: " + ex.getMessage()); + } +} diff --git a/src/main/java/com/example/bcsd/exceptions/InvalidRequestException.java b/src/main/java/com/example/bcsd/exceptions/InvalidRequestException.java new file mode 100644 index 00000000..2f157af1 --- /dev/null +++ b/src/main/java/com/example/bcsd/exceptions/InvalidRequestException.java @@ -0,0 +1,7 @@ +package com.example.bcsd.exceptions; + +public class InvalidRequestException extends RuntimeException { + public InvalidRequestException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/exceptions/ResourceNotFoundException.java b/src/main/java/com/example/bcsd/exceptions/ResourceNotFoundException.java new file mode 100644 index 00000000..c2106f33 --- /dev/null +++ b/src/main/java/com/example/bcsd/exceptions/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.bcsd.exceptions; + +public class ResourceNotFoundException extends RuntimeException{ + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/bcsd/models/Article.java b/src/main/java/com/example/bcsd/models/Article.java new file mode 100644 index 00000000..ef31f43f --- /dev/null +++ b/src/main/java/com/example/bcsd/models/Article.java @@ -0,0 +1,86 @@ +package com.example.bcsd.models; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +public class Article { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "author_id") + private Member author; + + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "board_id") + private Board board; + + private String title; + private String content; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public Article() { + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Member getAuthor() { + return author; + } + + public void setAuthor(Member author) { + this.author = author; + } + + public Board getBoard() { + return board; + } + + public void setBoard(Board board) { + this.board = board; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/models/Board.java b/src/main/java/com/example/bcsd/models/Board.java new file mode 100644 index 00000000..8bc3738d --- /dev/null +++ b/src/main/java/com/example/bcsd/models/Board.java @@ -0,0 +1,61 @@ +package com.example.bcsd.models; + +import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity + +public class Board { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + private String name; + + @OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private List
articles = new ArrayList<>(); + public Board() { + + } + + public Board(int id, String name) { + this.id = id; + this.name = name; + } + + public void addArticle(Article article) { + articles.add(article); + article.setBoard(this); + } + + public void removeArticle(Article article) { + articles.remove(article); + article.setBoard(null); + } + + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List
getArticles() { + return articles; + } + + public void setArticles(List
articles) { + this.articles = articles; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/models/Member.java b/src/main/java/com/example/bcsd/models/Member.java new file mode 100644 index 00000000..481a2431 --- /dev/null +++ b/src/main/java/com/example/bcsd/models/Member.java @@ -0,0 +1,83 @@ +package com.example.bcsd.models; + +import jakarta.persistence.*; + +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + private String account; + private String name; + private String email; + private String password; + + @OneToMany(mappedBy = "author", fetch = FetchType.LAZY) + private List
articles = new ArrayList<>(); + + public Member(int id, String account, String name, String email, String password) { + this.id = id; + this.account = account; + this.name = name; + this.email = email; + this.password = password; + } + + public Member() { + + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List
getArticles() { + return articles; + } + + public void setArticles(List
articles) { + this.articles = articles; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/repositories/ArticleRepository.java b/src/main/java/com/example/bcsd/repositories/ArticleRepository.java new file mode 100644 index 00000000..a7b78417 --- /dev/null +++ b/src/main/java/com/example/bcsd/repositories/ArticleRepository.java @@ -0,0 +1,15 @@ +package com.example.bcsd.repositories; + +import com.example.bcsd.models.Article; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ArticleRepository extends JpaRepository { + + List
findByBoardId(int boardId); + + long countByAuthorId(int authorId); +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/repositories/BoardRepository.java b/src/main/java/com/example/bcsd/repositories/BoardRepository.java new file mode 100644 index 00000000..660637c3 --- /dev/null +++ b/src/main/java/com/example/bcsd/repositories/BoardRepository.java @@ -0,0 +1,9 @@ +package com.example.bcsd.repositories; + +import com.example.bcsd.models.Board; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BoardRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/repositories/MemberRepository.java b/src/main/java/com/example/bcsd/repositories/MemberRepository.java new file mode 100644 index 00000000..cf21287f --- /dev/null +++ b/src/main/java/com/example/bcsd/repositories/MemberRepository.java @@ -0,0 +1,13 @@ +package com.example.bcsd.repositories; + +import com.example.bcsd.models.Member; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface MemberRepository extends JpaRepository { + Optional findByEmail(String email); + Optional findByAccount(String account); +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/services/MemberService.java b/src/main/java/com/example/bcsd/services/MemberService.java new file mode 100644 index 00000000..33ba8db3 --- /dev/null +++ b/src/main/java/com/example/bcsd/services/MemberService.java @@ -0,0 +1,93 @@ +package com.example.bcsd.services; + +import com.example.bcsd.exceptions.DataConflictException; +import com.example.bcsd.exceptions.InvalidRequestException; +import com.example.bcsd.exceptions.ResourceNotFoundException; +import com.example.bcsd.models.Member; +import com.example.bcsd.repositories.ArticleRepository; +import com.example.bcsd.repositories.MemberRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Service +public class MemberService { + + private final MemberRepository memberRepository; + private final ArticleRepository articleRepository; + + @Autowired + public MemberService(MemberRepository memberRepository, ArticleRepository articleRepository) { + this.memberRepository = memberRepository; + this.articleRepository = articleRepository; + } + + @Transactional + public Member createMember(Member member) { + if (member.getName() == null || member.getName().isBlank() || + member.getEmail() == null || member.getEmail().isBlank() || + member.getPassword() == null || member.getPassword().isBlank()) { + throw new InvalidRequestException("사용자 생성 요청 시 필수 값이 누락되었습니다. (name, email, password)"); + } + + memberRepository.findByEmail(member.getEmail()).ifPresent(m -> { + throw new DataConflictException("이미 사용 중인 이메일입니다: " + member.getEmail()); + }); + + return memberRepository.save(member); + } + + @Transactional(readOnly = true) + public Member getMemberById(int id) { + return memberRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("ID가 " + id + "인 사용자를 찾을 수 없습니다.")); + } + + @Transactional(readOnly = true) + public List getAllMembers() { + return memberRepository.findAll(); + } + + @Transactional + public void updateMember(int id, Member memberUpdateData) { + Member existingMember = getMemberById(id); + + if (memberUpdateData.getName() != null && memberUpdateData.getName().isBlank()) { + throw new InvalidRequestException("사용자 이름은 비워둘 수 없습니다."); + } + if (memberUpdateData.getEmail() != null && memberUpdateData.getEmail().isBlank()) { + throw new InvalidRequestException("이메일은 비워둘 수 없습니다."); + } + if (memberUpdateData.getPassword() != null && memberUpdateData.getPassword().isBlank()) { + throw new InvalidRequestException("비밀번호는 비워둘 수 없습니다."); + } + + if (memberUpdateData.getEmail() != null && !memberUpdateData.getEmail().equalsIgnoreCase(existingMember.getEmail())) { + Optional memberWithNewEmail = memberRepository.findByEmail(memberUpdateData.getEmail()); + if (memberWithNewEmail.isPresent() && memberWithNewEmail.get().getId() != id) { + throw new DataConflictException("이미 다른 사용자가 사용 중인 이메일입니다: " + memberUpdateData.getEmail()); + } + existingMember.setEmail(memberUpdateData.getEmail()); + } + + if (memberUpdateData.getName() != null && !memberUpdateData.getName().isBlank()) { + existingMember.setName(memberUpdateData.getName()); + } + if (memberUpdateData.getPassword() != null && !memberUpdateData.getPassword().isBlank()) { + existingMember.setPassword(memberUpdateData.getPassword()); + } + } + + @Transactional + public void deleteMember(int id) { + getMemberById(id); + + if (articleRepository.countByAuthorId(id) > 0) { + throw new InvalidRequestException("ID가 " + id + "인 사용자는 작성한 게시물이 있어 삭제할 수 없습니다."); + } + memberRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/bcsd/services/PostService.java b/src/main/java/com/example/bcsd/services/PostService.java new file mode 100644 index 00000000..99e1d0a4 --- /dev/null +++ b/src/main/java/com/example/bcsd/services/PostService.java @@ -0,0 +1,101 @@ +package com.example.bcsd.services; + +import com.example.bcsd.exceptions.InvalidRequestException; +import com.example.bcsd.exceptions.ResourceNotFoundException; +import com.example.bcsd.models.Article; +import com.example.bcsd.models.Board; +import com.example.bcsd.models.Member; +import com.example.bcsd.repositories.ArticleRepository; +import com.example.bcsd.repositories.BoardRepository; +import com.example.bcsd.repositories.MemberRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class PostService { + private final ArticleRepository articleRepository; + private final BoardRepository boardRepository; + private final MemberRepository memberRepository; + + public PostService(ArticleRepository articleRepository, BoardRepository boardRepository, MemberRepository memberRepository) { + this.articleRepository = articleRepository; + this.boardRepository = boardRepository; + this.memberRepository = memberRepository; + } + + @Transactional(readOnly = true) + public List
getArticlesByBoard(int boardId) { + if (!boardRepository.existsById(boardId)) { + throw new ResourceNotFoundException("ID가 " + boardId + "인 게시판을 찾을 수 없습니다."); + } + return articleRepository.findByBoardId(boardId); + } + + @Transactional(readOnly = true) + public String getBoardName(int boardId) { + return boardRepository.findById(boardId) + .map(Board::getName) + .orElseThrow(() -> new ResourceNotFoundException("ID가 " + boardId + "인 게시판을 찾을 수 없습니다.")); + } + + @Transactional(readOnly = true) + public Article getArticleById(int id) { + Article article = articleRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("ID가 " + id + "인 게시물을 찾을 수 없습니다.")); + return article; + } + + @Transactional + public void createArticle(Article article) { + if (article.getAuthor() == null || article.getAuthor().getId() == 0 || + article.getBoard() == null || article.getBoard().getId() == 0 || + article.getTitle() == null || article.getTitle().isBlank() || + article.getContent() == null || article.getContent().isBlank()) { + throw new InvalidRequestException("게시물 생성 요청 시 필수 값이 누락되었습니다. (authorId, boardId, title, content)"); + } + + Member author = memberRepository.findById(article.getAuthor().getId()) + .orElseThrow(() -> new InvalidRequestException("ID가 " + article.getAuthor().getId() + "인 사용자를 찾을 수 없습니다.")); + + Board board = boardRepository.findById(article.getBoard().getId()) + .orElseThrow(() -> new InvalidRequestException("ID가 " + article.getBoard().getId() + "인 게시판을 찾을 수 없습니다.")); + + article.setAuthor(author); + + board.addArticle(article); + boardRepository.save(board); + } + + @Transactional + public void updateArticle(int id, Article articleUpdateData) { + Article existingArticle = getArticleById(id); + + if (articleUpdateData.getTitle() != null) { + if (articleUpdateData.getTitle().isBlank()) { + throw new InvalidRequestException("게시물 제목은 비워둘 수 없습니다."); + } + existingArticle.setTitle(articleUpdateData.getTitle()); + } + + if (articleUpdateData.getContent() != null) { + if (articleUpdateData.getContent().isBlank()) { + throw new InvalidRequestException("게시물 내용은 비워둘 수 없습니다."); + } + existingArticle.setContent(articleUpdateData.getContent()); + } + existingArticle.setUpdatedAt(java.time.LocalDateTime.now()); + } + + @Transactional + public void deleteArticle(int id) { + Article article = getArticleById(id); + Board board = article.getBoard(); + if (board != null) { + board.getArticles().remove(article); + } else { + articleRepository.delete(article); + } + } +} \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 00000000..67ba52a6 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,21 @@ + + + + + Login Form + + +

Login Form

+
+ +

+ + +

+ + +
+ +

Don't have an account? Sign up here

+ + diff --git a/src/main/resources/templates/posts.html b/src/main/resources/templates/posts.html new file mode 100644 index 00000000..948e69b2 --- /dev/null +++ b/src/main/resources/templates/posts.html @@ -0,0 +1,15 @@ + + + + + Board Posts + + +

+
    +
  • + +
  • +
+ + diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 00000000..ba538275 --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,30 @@ + + + + + 회원가입 + + +

회원가입

+
+ +

+ + +

+ + +

+ + +

+ + +

+ + +
+ +

이미 계정이 있으신가요? 로그인

+ +