{"id":27531310,"url":"https://github.com/simhani1/spring-springboot_with_jpa1","last_synced_at":"2025-06-28T17:32:33.330Z","repository":{"id":167525824,"uuid":"591958816","full_name":"simhani1/Spring-SpringBoot_with_JPA1","owner":"simhani1","description":"실전! 스프링 부트와 JPA 활용","archived":false,"fork":false,"pushed_at":"2023-01-30T08:27:45.000Z","size":568,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-19T05:26:03.180Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simhani1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-01-22T13:41:37.000Z","updated_at":"2023-04-09T18:51:31.000Z","dependencies_parsed_at":"2023-05-22T19:30:28.778Z","dependency_job_id":null,"html_url":"https://github.com/simhani1/Spring-SpringBoot_with_JPA1","commit_stats":null,"previous_names":["simhani1/spring-springboot_with_jpa1"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/simhani1/Spring-SpringBoot_with_JPA1","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simhani1%2FSpring-SpringBoot_with_JPA1","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simhani1%2FSpring-SpringBoot_with_JPA1/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simhani1%2FSpring-SpringBoot_with_JPA1/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simhani1%2FSpring-SpringBoot_with_JPA1/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simhani1","download_url":"https://codeload.github.com/simhani1/Spring-SpringBoot_with_JPA1/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simhani1%2FSpring-SpringBoot_with_JPA1/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262468964,"owners_count":23316227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-18T17:50:10.861Z","updated_at":"2025-06-28T17:32:33.324Z","avatar_url":"https://github.com/simhani1.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003ch2\u003e실전! 스프링 부트와 JPA 활용\u003c/h2\u003e\n\u003c/div\u003e\n\n#### gradle 의존관계 확인법\n- 터미널로 이동하여 `./gradlew dependecies`를 입력하면 의존관계가 출력된다.\n- slf4j: 로그를 찍는 인터페이스 모음이라고 생각하면 된다.\n\n#### 스프링 부트 thymeleaf viewName 자동 매칭\n- 스프링 부트는 templates 폴더 내부의 html파일명을 자동으로 매칭시켜준다.\n\n#### 꿀팁!\n- `implementation 'org.springframework.boot:spring-boot-devtools'`를 의존성으로 추가하면 html 파일을 `build -\u003e recompile`만 실행해주면 실시간으로 변화를 볼 수 있다.\n\n#### 도메인 주도 개발\n**기능 목록**\n- 회원 기능\n    - 회원 등록\n    - 회원 조회\n- 상품 기능\n    - 상품 등록\n    - 상품 수정\n    - 상품 조회\n- 주문 기능\n    - 상품 주문\n    - 주문 내역 조회\n    - 주문 취소\n- 기타 요구사항\n    - 상품은 재고 관리가 필요하다.\n    - 상품의 종류는 도서, 음반, 영화가 있다.\n    - 상품을 카테고리로 구분할 수 있다.\n    - 상품 주문시 배송 정보를 입력할 수 있다.\n\n![img.png](img_1/img.png)\n\n\u003e 참고: 외래키가 있는 곳을 연관관계의 주인으로 정해라\u003cbr\u003e\n\u003e 외래키가 없는 곳을 주인으로 정하면, 외래키 값이 업데이트 되면서 관리와 유지보수가 어렵고, 추가적으로 별도의 업데이트 쿼리가 발생하는 성능 문제도 있다.\n\n#### 엔티티 개발\n- 실무에서는 Setter는 꼭 필요한 경우에만 사용하는 것을 추천한다.\n- Setter를 막 열어두면 가까운 미래에 인티티가 왜 변경되는지 추적하기가 힘들어진다.\n- 따라서 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.\n\n#### 엔티티 설계시 주의점\n- 엔티티에는 가급적 setter를 사용하지 말자\n- 모든 연관관계는 지연로딩으로 설정\n  - 즉시로딩은 예측이 어렵고 어떤 SQL이 실행될지 추적하기 어렵다.\n  - 실무에서 모든 연관관계는 지연로딩으로 설정해야 한다.\n  - JPQL을 실행할 때 N+1문제가 자주 발생한다.\n- 컬렉션은 필드에서 초기화하자\n  - null문제에서 안전하다\n  - 하이버네이트는 엔티티를 영속화할 때 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다.\n- 테이블 칼럼명 생성 전략\n\n#### 단위 테스트\n- test와 main의 설정 파일을 구분해서 작동시킬 수 있다.\n- 따라서 ApplicationContext.yml파일을 test에도 따로 두고 실행시키는 것이 좋다.\n- 이때 DB에 실제로 연결하는 것은 별로 권장하지 않는다. \n- Mock객체를 사용하여 테스트를 하면 된다.\n- 하지만 스프링 부트는 가짜 메모리 DB를 제공해주기 때문에 따로 설정할 것이 없다.\n\n#### 도메인 모델 패턴\n- 주문 서비스의 주문과 주문 취소 메서드를 보면 비즈니스 로직 대부분이 엔티티에 있다.\n- 서비스 계층은 단순히 엔티티에 필요한 요청을 위임하는 역할을 한다. \n- 이처럼 엔티티가 비즈니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것을 도메인 모델 패턴이라고 한다.\n- 반대로 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분의 비즈니스 로직을 처리하는 것을 드랜잭션 스크립터 패턴이라고 한다.\n\n####  변경 감지와 병합(merge)\n*정말 중요*\n- 준영속 엔티티\n  - 영속성 컨텍스트가 더이상 관리하지 않는 엔티티를 말한다.\n  - `itemService.saveItem(book)`에서 수정을 시도하는 `Book`객체가 해당된다.\n  - 해당 객체는 DB에 한번 저장되어서 식별자가 존재한다. 이렇게 임의로 만들어낸 엔티티도 기존 식별자를 가지고 있으면 준영속 엔티티로 볼 수 있다.\n- 방법1\n  - 영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법\n  - 트랜잭션 안에서 엔티티를 다시 조회, 변경할 값 선택 -\u003e 트랜잭션 커밋 시점에 변경 감시가 동작해서 DB에 update sql을 실행한다.\n  \n  ```java\n      @Transactional\n    // 변경감지를 사용하여 준영속 엔티티를 수정하는 방법\n    public void updateItem(Long itemId, Book param) {\n        Item findItem = itemRepository.findOne(itemId);\n        findItem.setPrice(param.getPrice());\n        findItem.setName(param.getName());\n        findItem.setStockQuantity(param.getStockQuantity());\n    }\n  ```\n  \n- 방법2\n  - 병합 사용\n  - 위에서 하는 방식과 동일하다.\n  \n  ```java\n    public void save(Item item) {\n        if (item.getId() == null) {\n            em.persist((item));  // 실제 추가\n        } else {\n            em.merge(item);  // 업데이트라고 생각을 하자\n        }\n    }\n  ```\n\n\u003e **주의사항**\n\u003e \n\u003e 변경 감지 기능을 사용하면 원하는 속성만 선택해서 값을 변경할 수 있다. \n\u003e \n\u003e 하지만 병합을 사용하면 모든 속성의 값들이 변경된다. 병합시 값이 없으면 `null`로 업데이트가 될 수도 있으므로 위험하다.\n\u003e\n\u003e 따라서 머지를 안쓴다고 생각하고 하나하나 변경감지 방법을 사용하도록 작성하는 것이 맞다고 생각해라\n\n- 가장 좋은 방법\n  - 엔티티를 변경할 때는 항상 변경감지를 사용해라\n  - 컨트롤러에서 어설프게 엔티티를 생성하지 말고 차라리 DTO 객체, 아에 매개변수로 변경되는 값을 넘기는 방식을 사용하라\n  - 트랜잭션이 있는 서비스 계층에 식별자와 함께 변경할 데이터를 명확하게 전달해라\n  - 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경해라\n  - 트랜잭션 커밋 ㅎ시점에 변경 감지가 실행됩니다.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimhani1%2Fspring-springboot_with_jpa1","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimhani1%2Fspring-springboot_with_jpa1","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimhani1%2Fspring-springboot_with_jpa1/lists"}