Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
2be6659
feat: 1. /introduce/html로 요청하기
eighttontruck8 Nov 10, 2025
ef40bb1
feat: 2. /introduce/string?name=이름 출력하기
eighttontruck8 Nov 10, 2025
f91348e
feat: 3. JSON 응답하기
eighttontruck8 Nov 10, 2025
8c85ccd
feat: CRUP API 작성
eighttontruck8 Nov 10, 2025
1929b23
feat: GET (404 NOT FOUND)예외처리 추가
eighttontruck8 Nov 10, 2025
3ca1877
feat: PUT, DELETE ResponseEntity로 변경 후 예외처리 추가
eighttontruck8 Nov 10, 2025
bea25b7
feat: GET ResponseEntity로 변경 후 예외처리 추가
eighttontruck8 Nov 10, 2025
421f6cf
refactor: hello 예제를 MVC모델로 리팩토링
eighttontruck8 Nov 24, 2025
bba018e
refactor: DTO 추가
eighttontruck8 Nov 24, 2025
685259a
fix: 필요없는 코드 삭제
eighttontruck8 Nov 24, 2025
973e141
feat: Member 관련 코드 작성
eighttontruck8 Nov 24, 2025
832dabe
feat: MemberService.java 작성
eighttontruck8 Nov 24, 2025
0e4a15f
test: MemoryMemberRepositoryTest 추가
eighttontruck8 Nov 24, 2025
dd15a17
fix: Member 필드 추가
eighttontruck8 Nov 24, 2025
6fcc41a
feat: Article 도메인 작성
eighttontruck8 Nov 24, 2025
5137891
feat: board 도메인 작성
eighttontruck8 Nov 24, 2025
e99147c
feat: CRUD API 작성
eighttontruck8 Nov 24, 2025
267f349
feat: CRUD API 불러오기 성공(/posts 제외)
eighttontruck8 Nov 24, 2025
8fc22c3
feat: CRUD API 불러오기 성공(전부)
eighttontruck8 Nov 24, 2025
39592fb
feat: Jdbc 의존성 추가, MySQL 접근을 위한 .yml 파일 작성
eighttontruck8 Nov 25, 2025
3102c7e
feat: CRUD - GET 구현해야하는 기능 주석 추가
eighttontruck8 Nov 25, 2025
bc42497
feat: ArticleRowMapper 생성
eighttontruck8 Nov 25, 2025
e0a827d
feat: CRUD 中 GET 요청 Jdbc 리팩토링 완료. 다만, 샘플 데이터가 아직 없음
eighttontruck8 Nov 25, 2025
e63da7e
feat: a. GET /posts?boardId 완료
eighttontruck8 Nov 30, 2025
04f2fb0
fix: a. GET /posts?boardId -게시판이름이 나오도록 수정
eighttontruck8 Nov 30, 2025
048b34f
feat: b.GET /articles?boardId 추가
eighttontruck8 Dec 1, 2025
a0f0c62
fix: d. POST /articles (long->Long 타입 변경)
eighttontruck8 Dec 1, 2025
577d88c
feat: e. PUT /articles/{id} 추가
eighttontruck8 Dec 1, 2025
2c39380
feat: f. DELETE /articles/{id} 추가
eighttontruck8 Dec 1, 2025
9737281
feat: 1. 조회 예외처리 추가
eighttontruck8 Dec 1, 2025
1392bb5
refactor: member도 jdbc로 변경
eighttontruck8 Dec 1, 2025
2a2f42c
fix: 에러 픽스
eighttontruck8 Dec 1, 2025
e656ece
feat: 2. 수정 예외처리 - 409 이메일 중복
eighttontruck8 Dec 1, 2025
442ca10
fix : ArticleDTO 생성, article의 board_id가 제대로 뜨지 않는 이슈 해결
eighttontruck8 Dec 1, 2025
d8d580c
feat: 2. 수정 예외처리 - 400 존재하지 않는 사용자/게시판
eighttontruck8 Dec 1, 2025
6ef2a24
feat: 3. 생성 예외처리 - 400 missing field - member, article 완료
eighttontruck8 Dec 1, 2025
0f23e4d
refactor: board - jdbctemplate사용. feat: 3. 생성 예외처리 - 400 missing fiel…
eighttontruck8 Dec 1, 2025
761fdd9
feat: 3. 생성 예외처리 - 400 에러 존재하지 않는 사용자/게시판 참조
eighttontruck8 Dec 1, 2025
a92300a
feat: 4. 삭제 예외처리 - 400 RemainArticlesException
eighttontruck8 Dec 1, 2025
f256dc4
feat: 4. 삭제 예외처리 - 게시판까지 완료git add .git add .git add .
eighttontruck8 Dec 1, 2025
102741f
refactor: jpa 의존성 추가
eighttontruck8 Jan 4, 2026
0b3a894
refactor: member테이블 jpa 변경
eighttontruck8 Jan 4, 2026
7532a79
fix: setter 어노테이션 위치 수정
eighttontruck8 Jan 4, 2026
6ed39c0
refactor: board테이블 jpa 변경
eighttontruck8 Jan 4, 2026
584c45c
refactor: article테이블에 jpa 추가
eighttontruck8 Jan 4, 2026
2ac2e2f
refactor: BoardRepository 인터페이스 생성 및 분리, Jpa 구현
eighttontruck8 Jan 4, 2026
4fc9db8
remove: boardRepository 구현부 삭제
eighttontruck8 Jan 4, 2026
31c6853
remove: boardRepository 구현부 삭제
eighttontruck8 Jan 4, 2026
4d1d3e8
remove: board rowmapper 삭제
eighttontruck8 Jan 4, 2026
b0cc9ea
refactor: BoardService JPA 변경
eighttontruck8 Jan 4, 2026
f576b72
remove: articleRepository구현부 삭제
eighttontruck8 Jan 4, 2026
c8ca023
refactor: ArticleService JPA 변경
eighttontruck8 Jan 4, 2026
5455de2
fix: 자잘한 에러들 수정
eighttontruck8 Jan 4, 2026
64be0c4
refactor: memberRepository구현부 삭제 + JPA 변경
eighttontruck8 Jan 4, 2026
0a4351b
JPARepository -> entityManager로 변경하기 위한 커밋
eighttontruck8 Jan 5, 2026
8f37f46
feat: article 엔티티에 연관관계 추가
eighttontruck8 Jan 5, 2026
9103f74
feat: board 엔티티에 연관관계+부모 매핑, 영속성 추가
eighttontruck8 Jan 5, 2026
37b4ea3
feat: member엔티티에 양방향 매핑 추가
eighttontruck8 Jan 5, 2026
2af9bcd
refactor: JPA와 부모-자식에 맞게 service 및 controller 수정
eighttontruck8 Jan 5, 2026
fc74925
feat: jsonIgnore추가
eighttontruck8 Jan 5, 2026
80d0c44
feat: 테스트 코드 추가
eighttontruck8 Jan 5, 2026
98bab15
Initial commit
eighttontruck8 Jan 14, 2026
709f5b7
feat: 10주차까지의 과제
eighttontruck8 Jan 14, 2026
b55b520
feat: brypt 의존성 추가
eighttontruck8 Jan 14, 2026
73f2cca
feat: bcrypt 의존성 추가
eighttontruck8 Jan 14, 2026
da2d39e
feat: bcrypt 의존성 추가
eighttontruck8 Jan 14, 2026
8cbe136
refactor: bcrypt를 bean으로 등록
eighttontruck8 Jan 14, 2026
4fc91c8
feat: login() 구현
eighttontruck8 Jan 14, 2026
b67e4f9
feat: AuthController 생성
eighttontruck8 Jan 14, 2026
0feaa17
feat: 회원가입 및 로그인 기능 구현 완료
eighttontruck8 Jan 14, 2026
07d590b
refactor: @RequiredArgsConstructor 추가
eighttontruck8 Jan 15, 2026
c943699
feat: jwttokenDTO, tokenProvider 생성
eighttontruck8 Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# BCSD_BackEnd25-2-week10
비기너 10주차까지의 과제 백업
16 changes: 14 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,21 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'com.mysql:mysql-connector-j'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'

runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'
compileOnly 'org.projectlombok:lombok:1.18.38'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/example/bcsd/BcsdApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
public class BcsdApplication {

public static void main(String[] args) {
SpringApplication.run(BcsdApplication.class, args);

SpringApplication.run(BcsdApplication.class, args);
}

}
20 changes: 0 additions & 20 deletions src/main/java/com/example/bcsd/HelloController.java

This file was deleted.

63 changes: 63 additions & 0 deletions src/main/java/com/example/bcsd/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.example.bcsd.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;

import java.util.Collections;

@RequiredArgsConstructor
@Configuration
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // JWT 사용하므로 비활성화
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

// 엔드포인트별 접근 권한 설정
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll() // 로그인/회원가입은 열어두어야 접근 가능함...
.anyRequest().authenticated() // 이외 요청은 인증된 사용자만 접근 가능
)
.httpBasic(AbstractHttpConfigurer::disable) // Basic Auth 끄기
// JWT 필터 추가 (기존 UsernamePasswordAuthenticationFilter 이전에 실행)
.addFilterBefore(new JWTFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class)

// 로그인 필터 추가 (JWTFilter 실행 후 JWT 발급 처리)
.addFilterAfter(new LoginFilter(authenticationManager(), jwtUtil), JWTFilter.class)

// 세션을 사용하지 않음 (JWT 기반 인증이므로 STATELESS 모드 설정)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

return http.build();
}

// 프론트용
@Bean
public CorsConfigurationSource corsConfigurationSource() {
return request -> {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // 허용할 도메인
configuration.setAllowedMethods(Collections.singletonList("*")); // 모든 HTTP 메서드 허용
configuration.setAllowCredentials(true); // 인증 정보 포함 허용
configuration.setAllowedHeaders(Collections.singletonList("*")); // 모든 헤더 허용
configuration.setExposedHeaders(Collections.singletonList("Authorization")); // Authorization 헤더 노출
configuration.setMaxAge(3600L); // 1시간 동안 캐싱
return configuration;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.example.bcsd.controller;

import com.example.bcsd.domain.Article;
import com.example.bcsd.dto.ArticleDTO;

import com.example.bcsd.dto.ArticleResponse;
import com.example.bcsd.service.ArticleService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/articles")
public class ArticleApiController {
private final ArticleService articleService;

public ArticleApiController(ArticleService articleService) {
this.articleService = articleService;
}

// 1. POST /articles : 게시글 저장하기
@PostMapping
public Article create(@RequestBody ArticleDTO req){ // ??
return articleService.create(req);
}

// 2. GET /articles?boardId={boardId} : 한 게시판의 모든 article 조회하기
@GetMapping
public List<Article> getAllArticlesByBoard(@RequestParam(required = false) Long boardId){
return articleService.getAllArticlesByBoard(boardId);
}
@GetMapping("/by-board")
public List<ArticleResponse> getByBoard(@RequestParam Long boardId) {
return articleService.getAllArticlesByBoard(boardId)
.stream()
.map(ArticleResponse::from)
.toList();
}
// 3. GET /articles/{id} : 아이디로 article 찾기
@GetMapping("/{id}")
public Article getOne(@PathVariable Long id){
return articleService.getOne(id);
}

// 4. PUT : 게시글 수정하기
@PutMapping("/{id}")
public Article update(@PathVariable Long id,
@RequestBody ArticleDTO req){
return articleService.update(req, id);
}

// 5. DELETE /articles/{id} : id로 삭제하기
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller의 메서드명이 조금더 명확해도 좋을거같아요

아이디로 삭제 : delete -> deleteById

아이디로 Article 조회 : getOne -> getById

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

참고하여 리팩토링 진행하겠습니다. 감사합니다!

articleService.delete(id);
// 예외는 service에서만!
return ResponseEntity.noContent().build(); // 204
}

}
32 changes: 32 additions & 0 deletions src/main/java/com/example/bcsd/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.bcsd.controller;

import com.example.bcsd.dto.MemberDTO;
import com.example.bcsd.service.MemberService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/auth")
public class AuthController {

private final MemberService memberService;

public AuthController(MemberService memberService) {
this.memberService = memberService;
}

@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody MemberDTO req) {
memberService.login(req);
return ResponseEntity.ok("login success");
}

@PostMapping("/signup")
public ResponseEntity<String> signup(@RequestBody MemberDTO req) {
memberService.signup(req);
return ResponseEntity.ok("signup success");
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/example/bcsd/controller/BoardController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.bcsd.controller;

import com.example.bcsd.domain.Board;
import com.example.bcsd.dto.BoardDTO;
import com.example.bcsd.dto.BoardResponse;
import com.example.bcsd.service.BoardService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/board")
public class BoardController {
private final BoardService boardService;

public BoardController(BoardService boardService) {
this.boardService = boardService;
}

// 1. POST /board : 게시판 신규 생성하기
@PostMapping
public Board create(@RequestBody BoardDTO req){
return boardService.create(req);
}

// 2. DELETE /boardId/{id} : 게시판 id로 삭제하기
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
boardService.delete(id);
return ResponseEntity.noContent().build(); // 204
}

// 3. GET /board/{id} : 게시판 id로 게시글 조회하기
@GetMapping("/{id}")
public BoardResponse getOne(@PathVariable Long id) {
// return boardService.getOne(id);
return BoardResponse.from(boardService.getOne(id));
}
}
45 changes: 45 additions & 0 deletions src/main/java/com/example/bcsd/controller/HelloController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// controller는 어떤 로직을 호출할건지 요청을 받는다.
// 즉, controller -> service
package com.example.bcsd.controller;

import com.example.bcsd.dto.HelloDTO;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/introduce")
public class HelloController {

// 1) /introduce/html -> introduce.html 반환
@GetMapping("/html")
public String hello(Model model) {
return "introduce";
}

// 2) /introduce/string?name=이름 -> "안녕하세요. 제 이름은 000 입니다."
@GetMapping("/string")
public String introduceString(@RequestParam String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}

// 3) /json에 json형태로 반환
@GetMapping("/json")
@ResponseBody
// <1> Map으로 JSON 수제로 만들기 -- 권장되는 방법이 아님
// public Map<String, Object> introduceJson() {
// return helloService.getHelloJson();
// }
// <2> 객체를 통해 JSON 자동 반환하기★
public HelloDTO getJson(){
HelloDTO hello = new HelloDTO();
hello.setAge(24);
hello.setName("윤해인");
return hello;
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/example/bcsd/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.bcsd.controller;

import com.example.bcsd.domain.Member;
import com.example.bcsd.dto.MemberDTO;
import com.example.bcsd.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
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;
}

// 1. member 생성
@PostMapping
public Member create(@RequestBody MemberDTO req) {
return memberService.create(req);
}

// 2. member 정보 수정
@PutMapping("/{id}")
public Member update(@PathVariable Long id,
@RequestBody MemberDTO req) {
return memberService.update(req, id);
}

// 3. member 삭제
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
memberService.delete(id);
return ResponseEntity.noContent().build(); // 204
}

// 4. member 조회
// 4-1. id로 조회
@GetMapping("/{id}")
public Member getOne(@PathVariable Long id) {
return memberService.getOne(id);
}

// 4-2. name으로 조회
@GetMapping("/search")
public List<Member> searchByName(@RequestParam String name) {
return memberService.searchByName(name);
}
}
30 changes: 30 additions & 0 deletions src/main/java/com/example/bcsd/controller/PostController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.bcsd.controller;

import com.example.bcsd.service.ArticleService;
import com.example.bcsd.service.BoardService;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/posts") // /posts?boardId={boardId}
public class PostController {
private final BoardService boardService;
private final ArticleService articleService;

public PostController(ArticleService articleService, BoardService boardService) {
this.boardService = boardService;
this.articleService = articleService;
}
Comment on lines +17 to 21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lombok라이브러리를 한번 알아봐보셔도 좋을거같아요.
Getter , Setter, 생성자등 패턴들을 어노테이션 하나로 간단하게 생성가능해요 (의존성 추가 필요)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lombok이 getter와 setter, 생성자를 대체할 수 있다-까지는 확인했는데, 아직은 실제로 써보는게 좋을 것 같아 그대로 두었습니다.
이번 9주차 때 같이 리팩토링 하겠습니다!


@GetMapping
public String posts(@RequestParam("boardId") Long boardId, Model model) {
String boardName = boardService.getBoardName(boardId);
model.addAttribute("boardName", boardName);
model.addAttribute("articles", articleService.getAllArticlesByBoard(boardId));
return "posts";
}
}
Loading