Spring Boot와 JPA로 게시판을 만들면서 겪었던 다양한 실수와,
그 원인, 그리고 올바른 해결 방법을 한 번에 정리합니다.
실제 코딩 예시와 함께, 초보자가 헷갈리기 쉬운 부분을 콕 집어 설명합니다!
JPA의 모든 데이터 조작(CRUD)은 영속성 컨텍스트라는 컨테이너에서 이루어집니다.
save() 또는 persist()로 새로운 엔티티를 저장findById(), findAll() 등으로 조회 (1차 캐시 활용)deleteById() 등으로 삭제영속성 컨텍스트란?
자바에서 1~100 사이의 랜덤 정수 생성:
Random random = new Random();
int randNum = random.nextInt(100) + 1; // 1~100테스트 데이터 100개 삽입 예시:
@BeforeAll
public void insertData() {
    Random random = new Random();
    for (int i = 0; i  findByTitle("1"); // 제목이 "1"과 정확히 일치하는 것만 조회제대로 하려면?
List findByTitleContaining(String keyword); // 제목에 keyword가 포함된 게시글 조회"1"이 포함된 모든 게시글을 찾으려면 Containing을 반드시 붙여야 한다.여러 조건, 정렬까지 하고 싶다면?
List findByTitleContainingOrContentContainingOrderByTitleDescContentDesc(String t, String c);잘못된 코드:
Board updated = boardRepo.findById(board.getSeq()).get().builder()
    .cnt(2L).content("수정").title("수정포스트").build();
boardRepo.save(updated);.builder()는 새 객체를 만드는 것!올바른 코드:
Board existing = boardRepo.findById(board.getSeq()).get();
existing.setTitle("수정포스트");
existing.setContent("수정");
existing.setCnt(2L);
boardRepo.save(existing);@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private BoardRepository boardRepo;
    @GetMapping("/board")
    public List getBoards() {
        return boardRepo.findAll();
    }
    @PostMapping("/board")
    public Board postBoard(@RequestBody Board board) {
        return boardRepo.save(board);
    }
    @PutMapping("/board")
    public Board putBoard(@RequestBody Board board) {
        Board existing = boardRepo.findById(board.getSeq()).orElseThrow();
        existing.setTitle(board.getTitle());
        existing.setContent(board.getContent());
        existing.setCnt(board.getCnt());
        return boardRepo.save(existing);
    }
    @DeleteMapping("/board/{seq}")
    public void deleteBoard(@PathVariable Long seq) {
        boardRepo.deleteById(seq);
    }
}/test/board : 전체 게시글 조회/test/board : 게시글 등록 (JSON body 필요)/test/board : 게시글 수정 (JSON body 필요, seq 필수)/test/board/{seq} : 게시글 삭제findById(seq)는 Optional를 반환.get(), 또는 .orElseThrow() 등 사용이 글이 Spring Boot + JPA 실습 중 헷갈리는 부분을 한 번에 정리하는 데 도움이 되길 바랍니다!
더 궁금한 점은 댓글로 남겨주세요 😊
Spring Data JPA에서 JpaRepository를 상속받은 인터페이스에는 @Repository 어노테이션이 필요하지 않습니다. 이는 Spring Data JPA의 내부 메커니즘 때문입니다. 아래에서 자세히 설명드리겠습니다.
@Repository가 필요 없을까?JpaRepository를 상속받는 인터페이스는 Spring이 자동으로 빈(Bean)으로 등록합니다.@EnableJpaRepositories가 활성화되면,JpaRepository를 상속받은 모든 인터페이스를 스캔하여 프록시 객체를 생성하고 빈으로 등록합니다.BoardRepository → BoardRepositoryImpl 프록시 생성)@Repository의 주요 기능 중 하나는 JPA 예외를 Spring의 DataAccessException 계층으로 변환하는 것입니다.JpaRepository를 상속하면 예외 변환이 자동으로 처리되므로 별도 어노테이션이 필요 없습니다.@Repository가 필요할까?CustomBoardRepositoryImpl)가 있다면,@Repository를 붙여야 Spring이 빈으로 등록합니다.JpaRepository를 상속한 인터페이스가 아닌 경우에 한합니다.JpaRepository 상속 시 (어노테이션 불필요)// ✅ @Repository 없어도 정상 동작
public interface BoardRepository extends JpaRepository {
    List findByTitleContaining(String keyword);
}@Repository  // ✅ 필수
public class CustomBoardRepositoryImpl implements CustomBoardRepository {
    // 사용자 정의 메서드 구현
}| 상황 | @Repository필요 여부 | 
|---|---|
| JpaRepository상속 | ❌ 불필요 | 
| 직접 구현한 Repository 클래스 | ✅ 필요 | 
@EnableJpaRepositoriesJpaRepository를 상속한 인터페이스를 스캔합니다.JpaRepositoriesRegistrar 클래스가 이 과정을 처리합니다[8].JpaRepository의 프록시 객체가 자동으로 PersistenceExceptionTranslationPostProcessor와 연동되어 예외를 변환합니다.결론:
JpaRepository를 상속했다면 @Repository를 생략해도 됩니다!
이는 Spring Data JPA가 이미 모든 것을 자동화했기 때문입니다 😊
Citations:
[1] https://jaehoney.tistory.com/250
[2] https://velog.io/@astrataraxia/JPA-Repository-%EC%83%81%EC%86%8D%EC%9D%84-%EB%B0%9B%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0
[3] https://learngoeson.tistory.com/63
[4] https://velog.io/@dltkdgns3435/SpringBoot-spring-data-jpa-%EC%82%AC%EC%9A%A9%EC%8B%9C-Repository-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%80-%EA%BC%AD-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80
[5] https://peonyf.tistory.com/entry/Spring-%EA%B2%8C%EC%8B%9C%ED%8C%90-Repository-%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C-QnA
[6] https://ohzzi.io/jpa-repository-no-repository-yes/
[7] https://creampuffy.tistory.com/179
[8] https://parkadd.tistory.com/106
[9] https://ttl-blog.tistory.com/1124
[10] https://sudo-minz.tistory.com/147