inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

172만명의 커뮤니티!! 함께 토론해봐요.

안녕하세요. 강의 수강 후, 토이프로젝트에 적용해보다가 Fake Repository에 관해 궁금한 것이 생겨 질문드립니다!

미해결

Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트

안녕하세요. 우선 강의 너무 잘 들었습니다. 설계를 개선하며 소형테스트를 풍부하게 작성할 수 있다는 것을 보고 이를 체화하려고 공부중인데요, 강의에서 배운것을 적용하다가 막힌 곳이 있어서 질문드립니다. 우선, 저는 강의와 유사하게 Repository를 추상화해서 프로덕션(~RepositoryImpl)과 테스트 환경(Fake~Repository)을 다르게 가져가고 있고, 막힌 부분은 아래와 같습니다. 프로덕션에서 groupBy를 사용하고, Dto로 바로 조회하는 방식으로 프로덕션 Repository에 코드를 작성한 부분이 있는데, 이를 Fake Repository에서 작성하려니까 막막하더라구요 왜 FakeRepository에 작성하려 하냐면, 제가 이해하기로는 FakeRepository를 구현해야 Service 레이어를 테스트할 때, 소형테스트로 작성할 수 있다고 이해를 했기 때문입니다. 막힌 부분에서 여러 고민해보다가, 다양한 조건절과 그룹핑이 들어간 쿼리는 어떻게 Fake Repository에 작성해서 테스트할 수 있을까? 하는 궁금증이 생겼습니다. 그래서 질문입니다. Fake Repository를 사용해서 일부 Service 레이어의 로직을 테스트할 수 있지만, 좀 더 복잡한 쿼리들은 Fake로 구현(stream, filter등을 사용해서)하기 어려우니, Fake로 구현하지 않고 중형 테스트로 테스트한다. 그리고 위의 기준이 소형테스트로 테스트할 지, 중형테스트로 테스트할 지 고려할 기준이 된다..? 저는 위와같이 생각이 이어지는데, 혹시 옳은 생각일까요? 아님 열심히 Fake로 구현을 해봐야하는 것일까요? 그것도 아니라면 다른 방법이 있을까요? 강사님은 복잡한 쿼리들을 어떻게 하시는지 궁금합니다..! 열심히 강의해주셨는데, 막상 적용해보려니 쉽지 않아서 민망합니다.. 답변 미제공 강의지만, 호옥시나 하는 마음에 질문 남깁니다.🥹 좋은 강의 감사드립니다

  • spring
  • tdd
  • jpa
  • 소프트웨어-테스트
  • unittest
19 댓글 2 좋아요 0 조회수 612

캐시를 포함한 Service Layer 테스트 가이드

미해결

Practical Testing: 실용적인 테스트 가이드

안녕하세요. 우빈님! 캐시를 처음 적용해 보아서 캐시를 포함한 비지니스 로직을 어떻게 작성하는 것이 좋을까? 에 대한 고민이 있어 글 남깁니다...! AS IS @Transactional public UosRestaurantMenuResponse getUosRestaurantMenu(UosRestaurantInput input) { // 학식 조회 UosRestaurant findUosRestaurant = uosRestaurantRepository.findByCrawlingDateAndRestaurantNameAndMealType(input.getDate(), input.getRestaurantName(), input.getMealType()) .orElseThrow(() -> new UosRestaurantMenuException(UosRestaurantMenuException.NOT_FOUND_MENU)); // 조회수 증가 findUosRestaurant.increaseView(); return UosRestaurantMenuResponse.of(findUosRestaurant); } TO BE @Transactional public UosRestaurantMenuResponse getUosRestaurantMenu(UosRestaurantInput input) { // 캐시에서 학식 조회 Optional<CacheUosRestaurant> cacheUosRestaurant = cacheUosRestaurantRepository .findById(CacheUosRestaurant.createId(input)); // 캐시에 학식이 존재하면 if(cacheUosRestaurant.isPresent()) { // 조회수 증가 cacheUosRestaurant.get().increaseView(); CacheUosRestaurant saveCacheRestaurant = cacheUosRestaurantRepository.save(cacheUosRestaurant.get()); return UosRestaurantMenuResponse.of(saveCacheRestaurant); } // 학식 조회 UosRestaurant findUosRestaurant = uosRestaurantRepository.findByCrawlingDateAndRestaurantNameAndMealType(input.getDate(), input.getRestaurantName(), input.getMealType()) .orElseThrow(() -> new UosRestaurantMenuException(UosRestaurantMenuException.NOT_FOUND_MENU)); // 조회수 증가 findUosRestaurant.increaseView(); // 캐시에 저장 cacheUosRestaurantRepository.save(CacheUosRestaurant.of(findUosRestaurant)); return UosRestaurantMenuResponse.of(findUosRestaurant); } 캐시에 대한 로직이 추가될 때 위와 같이 하나의 서비스 레이어 메소드에 작성하는 것이 좋은걸까요? 캐시에 대한 테스트 코드를 작성할 때 캐시를 사용할 때와 캐시를 사용하지 않을 때를 상황을 구분하여 작성 하는 것이 맞을까요..?(그것이 좋겠죠....? -> 기존 비지니스 로직 테스트 코드를 수정하는것이 최선일까? 에대한 의문이 들어서 질문 드렸습니다.) 스프링에서 CacheManager를 이용하여 @Cacheable 을 활용하는 방법이 있는데, CrudRepository를 사용하는 방법과 CacheManager를 사용하는 방법 중 어느 것이 더 좋은(?) 방법 인지 말씀주시면 감사하겠습니다. 4. 마지막으로 캐시를 사용할 때 깔끔하게 비지니스 로직을 작성할 수 있는 노하우 말씀주시면 감사하겠습니다.!!! 다소 질문이 난해한데 너그럽게 이해해주시면 감사하겠습니다.ㅠㅠ 좋은 강의 잘 듣고 있습니다. 다음 강의도 기대할께요^^!! 감사합니다.

  • spring
  • tdd
  • jpa
  • mockito
  • 소프트웨어-테스트
  • junit5
개발하는쿼카 댓글 2 좋아요 1 조회수 448

여러 패키지들 does not exist 오류

미해결

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

안녕하세요 김영한님! 강의 아주 잘 듣고있습니다!! 현재 김영한님 첫번째 강의를 다 듣고 저 혼자 만들어보고 싶은게 생겨서 만드는중인데요 현재 제가 군대에 있어서 사지방에서 코딩을 하느라 인텔리제이를 사용하지 못하고 깃허브에서 제공하는 codespaces와 gitpod을 사용하여 vscode 환경에서 코딩하는 중입니다. 근데 jpa의 JpaRepository나 lombok 혹은org.springframework.boot.autoconfigure.SpringBootApplication 이런 패키지들이 계속 does not exist라고 뜹니다. 김영한님꺼 따라할때는 잘 됐는데 이제 제가 혼자서 만들고 싶은거 만드려니 확실하지도 않고 실행도 잘 안돼서 패키지를 못읽는건지도 잘 모르겠습니다. 현재 build.gradle에 의존성도 다 넣어있고 vscode의 패키지 설치에도 java extention pack, lombok, spring extention pack또 다 깔았습니다. 당연 재설치도 해보았구요! 구글링을 지금 며칠째 하고있는데 도저히 안되서 여기에 질문 올려봅니다... springboot version : 3.1.3 jdk version : 17

  • java
  • spring
  • mvc
  • springboot
  • pleasehelpme
  • 군대
이정환 댓글 3 좋아요 0 조회수 3647

@Transactional을 붙였을 때"만" Stock 감소 검증에 성공합니다

미해결

Practical Testing: 실용적인 테스트 가이드

각 테스트 케이스들을 검증하고, 이에 대해 값을 비워주기 위해 두 가지 방식을 소개해주셨습니다. 1) tearDown()을 사용해서 after each로 모두 날려주는 방식과 2) 클래스 레벨에서 테스트 클래스에 @Transactional 을 붙이는 방식 강의와 동일하게 "001" : 1000 : 2개, "002" : 3000 : 2개, "003" : 5000 가정 하에, "001", "001", "002", "003" 순으로 주문했다 하였습니다. 하지만, 문제는 1)을 사용하였을 때는 아래와 같은 에러가 발생하구요, 2)을 사용했을 때는 테스트 케이스가 정상 검증 됩니다. [Extracted: productNumbeㅏr, quantity] Expecting actual: [("001", 2), ("002", 2)] to contain exactly in any order: [("001", 0), ("002", 1)] elements not found: [("001", 0), ("002", 1)] and elements not expected: [("001", 2), ("002", 2)] 올려주신 깃허브 코드 5-7 또한 참고해보았으며, 코드 복붙까지 시도했는데 AfterEach 방식에서만 검증 실패가 등장하네요 // 보여주신 디버깅 방식 참조하여 디버깅도 해보았는데, 제 눈에는,,, 이상이 없었습니다... 미천한 디버깅 실력에 부끄러움만 앞서지만, 왜 트랜잭션 어노테이션에서만 검증 성공인지 의아합니다. 혹시 답변 가능하시다면 부탁드립니다,,! 코드 첨부합니다. OrderService package sample.cafekiosk.api.service.order; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import sample.cafekiosk.api.controller.order.OrderCreateRequest; import sample.cafekiosk.domain.order.Order; import sample.cafekiosk.domain.product.Product; import sample.cafekiosk.domain.product.ProductRepository; import sample.cafekiosk.domain.product.ProductType; import sample.cafekiosk.domain.stock.Stock; import sample.cafekiosk.domain.stock.StockRepository; import java.time.LocalDateTime; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class OrderService { // Product를 가져와야 하므로 의존 private final ProductRepository productRepository; private final OrderRepository orderRepository; private final StockRepository stockRepository; public OrderResponse createOrder(OrderCreateRequest request, LocalDateTime registeredDateTime) { List<String> productNumbers = request.getProductNumbers(); List<Product> products = findProductsBy(productNumbers); deductStockQuantities(products); Order order = Order.create(products, registeredDateTime); Order savedOrder = orderRepository.save(order); return OrderResponse.of(savedOrder); } private void deductStockQuantities(List<Product> products) { List<String> stockProductNumbers = extractStockProductNumbers(products); Map<String, Stock> stockMap = createStockMapBy(stockProductNumbers); Map<String, Long> productCountingMap = createCountingMapBy(stockProductNumbers); for (String stockProductNumber : new HashSet<>(stockProductNumbers)) { Stock stock = stockMap.get(stockProductNumber); int quantity = productCountingMap.get(stockProductNumber).intValue(); if (stock.isQuantityLessThan(quantity)) { throw new IllegalArgumentException("재고가 부족한 상품이 있습니다."); } stock.deductQuantity(quantity); } } private List<Product> findProductsBy(List<String> productNumbers) { List<Product> products = productRepository.findAllByProductNumberIn(productNumbers); Map<String, Product> productMap = products.stream() .collect(Collectors.toMap(Product::getProductNumber, p -> p)); return productNumbers.stream() .map(productMap::get) .collect(Collectors.toList()); } private static List<String> extractStockProductNumbers(List<Product> products) { return products.stream() .filter(product -> ProductType.containsStockType(product.getType())) .map(Product::getProductNumber) .collect(Collectors.toList()); } private Map<String, Stock> createStockMapBy(List<String> stockProductNumbers) { List<Stock> stocks = stockRepository.findAllByProductNumberIn(stockProductNumbers); return stocks.stream() .collect(Collectors.toMap(Stock::getProductNumber, s -> s)); } private static Map<String, Long> createCountingMapBy(List<String> stockProductNumbers) { return stockProductNumbers.stream() .collect(Collectors.groupingBy(p -> p, Collectors.counting())); } } OrderServiceTest package sample.cafekiosk.api.service.order; import jakarta.transaction.Transactional; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.web.bind.annotation.RestController; import sample.cafekiosk.api.controller.order.OrderCreateRequest; import sample.cafekiosk.domain.product.Product; import sample.cafekiosk.domain.product.ProductRepository; import sample.cafekiosk.domain.product.ProductSellingStatus; import sample.cafekiosk.domain.product.ProductType; import sample.cafekiosk.domain.stock.Stock; import sample.cafekiosk.domain.stock.StockRepository; import java.time.LocalDateTime; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; import static org.junit.jupiter.api.Assertions.*; import static sample.cafekiosk.domain.product.ProductSellingStatus.SELLING; import static sample.cafekiosk.domain.product.ProductType.*; @ActiveProfiles("test") @SpringBootTest //@Transactional class OrderServiceTest { @Autowired private OrderService orderService; @Autowired private ProductRepository productRepository; @Autowired private OrderProductRepository orderProductRepository; @Autowired private OrderRepository orderRepository; @Autowired private StockRepository stockRepository; @AfterEach void tearDown() { orderProductRepository.deleteAllInBatch(); productRepository.deleteAllInBatch(); orderRepository.deleteAllInBatch(); stockRepository.deleteAllInBatch(); } @DisplayName("재고와 관련된 상품이 포함되어 있는 주문번호 리스트를 받아 주문을 생성한다.") @Test void createOrderWithStock() { // given LocalDateTime registeredDateTime = LocalDateTime.now(); Product product1 = createProduct(BOTTLE, "001", 1000); Product product2 = createProduct(BAKERY, "002", 3000); Product product3 = createProduct(HANDMADE, "003", 5000); productRepository.saveAll(List.of(product1, product2, product3)); Stock stock1 = Stock.create("001", 2); Stock stock2 = Stock.create("002", 2); stockRepository.saveAll(List.of(stock1, stock2)); OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001", "001", "002", "003")) .build(); // when OrderResponse orderResponse = orderService.createOrder(request, registeredDateTime); // then assertThat(orderResponse.getId()).isNotNull(); assertThat(orderResponse) .extracting("registeredDateTime", "totalPrice") .contains(registeredDateTime, 10000); assertThat(orderResponse.getProducts()).hasSize(4) .extracting("productNumber", "price") .containsExactlyInAnyOrder( tuple("001", 1000), tuple("001", 1000), tuple("002", 3000), tuple("003", 5000) ); List<Stock> stocks = stockRepository.findAll(); assertThat(stocks).hasSize(2) .extracting("productNumber", "quantity") .containsExactlyInAnyOrder( tuple("001", 0), tuple("002", 1) ); } @DisplayName("주문 번호리스트를 받아 주문을 생성한다.") @Test void createOrder() { // GIVEN LocalDateTime n = LocalDateTime.now(); Product product1 = createProduct(HANDMADE, "001", 1000); Product product2 = createProduct(HANDMADE, "002", 2000); Product product3 = createProduct(HANDMADE, "003", 3000); productRepository.saveAll(List.of(product1, product2, product3)); OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001","002")) .build(); // WHEN OrderResponse orderResponse = orderService.createOrder(request,n ); // THEN assertThat(orderResponse.getId()).isNotNull(); assertThat(orderResponse) .extracting("registeredDateTime","totalPrice") .containsExactlyInAnyOrder(n,3000); assertThat(orderResponse.getProducts()).hasSize(2) .extracting("productNumber", "price") .containsExactlyInAnyOrder( tuple("001", 1000), tuple("002", 2000) ); } @DisplayName("중복되는 상품번호 리스트로 주문을 생성할 수 있다.") @Test void createOrderWiuthDuplicatedProductNumbers() { // GIVEN LocalDateTime n = LocalDateTime.now(); Product product1 = createProduct(HANDMADE, "001", 1000); Product product2 = createProduct(HANDMADE, "002", 2000); Product product3 = createProduct(HANDMADE, "003", 3000); productRepository.saveAll(List.of(product1, product2, product3)); OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001","001")) .build(); // WHEN OrderResponse orderResponse = orderService.createOrder(request,n ); // THEN assertThat(orderResponse.getId()).isNotNull(); assertThat(orderResponse) .extracting("registeredDateTime","totalPrice") .containsExactlyInAnyOrder(n,2000); assertThat(orderResponse.getProducts()).hasSize(2) .extracting("productNumber", "price") .containsExactlyInAnyOrder( tuple("001", 1000), tuple("001", 1000) ); } private Product createProduct(ProductType type, String productNumber,int price) { return Product.builder() .productNumber(productNumber) .type(type) .sellingStatus(SELLING) .name("아메리카노") .price(price) .build(); } } Stock package sample.cafekiosk.domain.stock; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import sample.cafekiosk.domain.BaseEntity; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class Stock extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; private String productNumber; private int quantity; @Builder public Stock(String productNumber, int quantity) { this.productNumber = productNumber; this.quantity = quantity; } public static Stock create(String s, int i) { return Stock.builder().productNumber(s).quantity(i).build(); } public boolean isQuantityLessThan(int quantity) { return this.quantity < quantity; } public void deductQuantity(int quantity) { if(this.quantity < quantity){ throw new IllegalArgumentException("주문 수량이 재고보다 많습니다."); } this.quantity-=quantity; } }

  • spring
  • tdd
  • jpa
  • mockito
  • 소프트웨어-테스트
  • junit5
1026baby 댓글 2 좋아요 1 조회수 653

중소,스타트업에서는 코딩테스트 안 보는 곳도 있나요?

해결됨

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

중소, 스트타업 채용공고 찾아봤는데 코딩테스트를 보는 곳도 있고 아닌 곳도 있는 거 같아서 무조건 보는 건 아닌가요?

  • javascript
  • node.js
  • express
  • docker
  • tdd
  • rest-api
  • nestjs
kql 1 댓글 1 좋아요 0 조회수 622

section23. 04-06 Mongoose 활용편 질문입니다.

해결됨

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

안녕하세요. MongoDB compass로 mongodb:// localhost:27017 로 접속시, 네임레졸루션으로 설정한 mydocker가 데이터베이스가 보이지 않습니다. 어떻게 하면 MongoDB compass에서 mydocker를 볼 수 있게 할 수 있는지 알려주시면 감사하겠습니다. <네임레졸루션으로 설정한 mydocker> mongoose .connect("mongodb://my-database:27017/mydocker") .then(() => console.log("Connected!")) .catch(() => console.log("Connection Failed")); app.listen(4000, () => { console.log("Back-end API is Open Now"); }); CLI로 MongoDB id "ce236b58a149"로 접속하여 확인하여 보면 mydocker가 확이, Postman으로 확인하여도 POST GET가 잘 됩니다만, MongoDB compass로 접속이 되지 않습니다. <CLI로 mydocker 데이터베이스 확인> hyunminyu@HYUNMINnoAir class % docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ce236b58a149 mongo:5 "docker-entrypoint.s…" 12 seconds ago Up 11 seconds 0.0.0.0:27017->27017/tcp backend-my-database-1 e3f0f648c4c3 backend-my-backend "docker-entrypoint.s…" 21 minutes ago Up 11 seconds 0.0.0.0:4000->4000/tcp backend-my-backend-1 hyunminyu@HYUNMINnoAir class % docker exec -it ce236b58a149 /bin/bash root@ce236b58a149:/# mongo MongoDB shell version v5.0.21 connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("181ced5f-7b45-4fd5-bb08-c90762778e68") } MongoDB server version: 5.0.21 ================ Warning: the "mongo" shell has been superseded by "mongosh", which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in an upcoming release. For installation instructions, see https://docs.mongodb.com/mongodb-shell/install/ ================ --- The server generated these startup warnings when booting: 2023-09-22T15:44:38.346+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem 2023-09-22T15:44:39.191+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted --- > show databases admin 0.000GB config 0.000GB local 0.000GB mydocker 0.000GB > use mydocker switched to db mydocker > show collections boards 한가지 의심스러운 점이 있습니다. docker-compose.yaml에서 아래의 27017:27017 포트포워딩을 주석처리하여 docker up을해도, MongoDB compass로 mongodb:// localhost:27017 로 접속이 되는점이 혹시 문제 원인과 관련이 있지 않을까 생각이 듭니다. ports: - 27017:27017 감사합니다.

  • javascript
  • node.js
  • express
  • docker
  • tdd
  • rest-api
  • nestjs
s-june.lee 댓글 2 좋아요 0 조회수 336

강의10-8 관련 질문입니다.

해결됨

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

products.service.ts // 2-2) 상품태그 등록 const tagNames = productTags.map((el) => el.replace('#', '')); const prevTags = await this.productsTagsService.findByNames({ tagNames }); const temp = []; tagNames.forEach((el) => { const isExists = prevTags.find((prevEl) => el === prevEl.name); if (!isExists) temp.push({ name: el }); }); const newTags = await this.productsTagsService.bulkInsert({ names: temp }); const tags = [...prevTags, ...newTags.identifiers]; productsTags.service.ts const newTags = await this.productsTagsService.bulkInsert({ names: temp }); const tags = [...prevTags, ...newTags.identifiers]; products.service.ts 에서 const tags = [...prevTags, ...newTags.identifiers]; 의 결과로 주석으로 {id: "전자제품ID", {id: "컴퓨터ID}, {id: "영등포ID"}} 이렇게 적어주셨는데 ...newTags.identifiers 는 결과가 저렇게 id로 들어가는게 이해되는데 ...prevTags 는 왜 id로 들어가는지 이해가 잘 안됩니다.. prevTags는 키가 id랑 name 두개 있는 객체인데 왜 저렇게 들어가는걸까요 ?? spread 연산자를 사용해서일까요 주석의 예시대로라면 {id: "전자제품ID", name: "전자제품"}, {id: "컴퓨터ID"}, {id: "영등포ID"}} 이런식으로 들어가야 하는거 아닌가요?? 감사합니다

  • javascript
  • node.js
  • express
  • docker
  • tdd
  • rest-api
  • nestjs
댓글 2 좋아요 0 조회수 362

크롤링 기사 기간 설정

해결됨

[신규 개정판] 이것이 진짜 크롤링이다 - 실전편 (인공지능 수익화)

안녕하세요 뉴스크롤링에서 크롤링 하고자 하는 뉴스의 기간을 정해주려면 response = requests.get(" https://search.naver.com/search.naver?where=news&sm=tab_jum&query=검색어 ") 위 코드의 " " 안에 뉴스기간을 옵션으로 설정하여 검색한 페이지의 URL을 긁어서 넣어주면 되는걸지요? 감사합니다.

  • python
  • 웹-크롤링
cherrykim90 댓글 2 좋아요 0 조회수 1203

안녕하세요! 왜 h2를 이용하면 중형 테스트로 보게 되는걸까요??

미해결

Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트

중형 테스트는 멀티 스레드, 멀티 프로세스를 허용하는 테스트라고 이해했는데 그 설명을 하시면서 "멀티 프로세스와 멀티 스레드를 사용할 수 있는 완화조건이 생기죠. 이는 다시 말해 h2 같은 테스트용 DB를 사용할 수 있게 된다는 의미입니다." 라고 하셔서요! 혹시 h2를 사용하려면 멀티 스레드나 멀티 프로세스 환경에 있어야 한다고 이해해도 되는걸까요? "h2는 멀티 스레드, 멀티 프로세스 환경에 있어야 하기 때문에 h2를 사용하는 순간 중형 테스트로 보면 된다"로 이해하면 될까요??

  • spring
  • tdd
  • jpa
  • 소프트웨어-테스트
  • unittest
무자비한 낭만주먹 댓글 2 좋아요 1 조회수 762

jdbc:h2:tcp://localhost/~/test로 연결시 오류가 발생합니다

미해결

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예 [질문 내용] 여기에 질문 내용을 남겨주세요. 최초로 jdbc:h2:~/test로 연결을 실행 한 뒤 ~/ test.mv .db 파일 생성을 확인하였습니다. 그러고 영한님께서 말씀해주신 대로 jdbc:h2:tcp://l ocalhost/~/test 로 연결했더니 다음과 같은 오류가 발생하네요.. 해결방안 답변해주시면 감사하겠습니다. 참고로 h2 버전은 강의와 같은 버전을 사용하였습니다. +) 추가로 삭제 후 동일버전, 하위버전으로 재설치도 해보았는데 여전히 같은 오류가 발생합니다.

  • java
  • spring
  • mvc
  • spring-boot
djqwkfj43u 댓글 2 좋아요 0 조회수 2178

복합키 끼리의 매핑 질문 드립니다.

해결됨

자바 ORM 표준 JPA 프로그래밍 - 기본편

안녕하세요. 현재 JPA강의를 통해 회사내 프로젝트중인 직장인 입니다. 실무 중에 강의의 복합키 매핑과는 조금 다른 내용이 있어 며칠을 고민하다 문의 드립니다. 각각 복합키를 가진 두 개의 테이블 이 있습니다. 두 테이블은 code라는 공통 컬럼이있고 다대일관계로 매핑 을 구성하려고 합니다. @EmbeddedId관계 를 이용해 구성하려고 합니다. code 컬럼만 매핑시키는 방법을 찾지 못해 문의 남겨드립니다. 현재 문제의 테이블입니다.(회사프로젝트 테이블이라 자세히 올려드리는 못하는 점 양해 부탁드립니다.) Company Table은 code, biznumber 두 개의 pk로 구성돼 있습니다. Contract Table 역시 code, module 두 개의 pk로 구성돼 있습니다. 저는 두 테이블에서 code라는 컬럼만 갖고 다대일 매핑 을 구현하고 싶습니다. 즉 복합키를 각각 가진 두 개의 테이블에서 각각 하나의 컬럼만을 이용해 다대일 매핑을 구현하고 싶습니다. 제가 에러 내용과 과정을 올리고싶은데 회사코드라 올리면 문제가 생길까 싶어 이렇게 말로 표현드려 죄송합니다.

  • java
  • jpa
차재욱 댓글 2 좋아요 1 조회수 977

웹스톰 사용자인데 cookie를 출력해보면

해결됨

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

console.log(req.headers.cookie); Webstorm-55941cca=39281e1b-d7ae; refreshToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4ZDM2YTc4Yy1mOWFiLTQ1NjgtODg0NS03MWUzOTQ0ODE1ZjMiLCJpYXQiOjE2OTUyMTI1NzYsImV4cCI6MTY5NjQyMjE3Nn0.8M9UkwgQXS7dGYpq3rxHnSF9TmoEnliY_wbZuhacqL0 이런식으로 출력이 되서 처음에 refreshToken 설정할때 Webstorm-55941cca=39281e1b-d7ae; 이 부분을 없에는 방법이 있을까요? 계속 replace("Webstorm-55941cca=39281e1b-d7ae; refreshToken=','') 이런식으로 없애야 하나요?

  • javascript
  • node.js
  • express
  • docker
  • tdd
  • rest-api
  • nestjs
송원 댓글 1 좋아요 0 조회수 237

섹션23 도커부분 건너띄고, 섹션24 먼저 들어도 될까요?

해결됨

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

도커를 수강하지 않고 섹션 24,25 수강해도 될까요? 도커 이후 수업에서는 도커를 사용해서 수업을 진행하나요?

  • javascript
  • node.js
  • express
  • docker
  • tdd
  • rest-api
  • nestjs
김주원 댓글 1 좋아요 0 조회수 223

토이프로젝트 깃헙 링크 어디있나요?

미해결

Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트

섹션 2 1부 실기 수업 - 어쨌든 테스트 코드 에서 사용하는 기존 프로젝트 위치가 혹시 어디 있나요?

  • spring
  • tdd
  • jpa
  • 소프트웨어-테스트
  • unittest
Jay 댓글 2 좋아요 1 조회수 1262

beforeEach를 추가했음에도 afterEach가 필요한 이유

해결됨

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 강의 마지막에 beforeEach 메소드를 추가하여 매번 객체를 생성하게 되잖아요. 그러면 굳이 afterEach() 메소드로 매번 clear할 필요가 없다고 생각했었는데 afterEach() 메소드를 주석 처리하면 오류가 나더라고요. 제 생각엔 MemoryMemberRepository의 store 변수가 static이기 때문에 beforeEach() 메소드로 매번 객체를 새로 생성하더라도 static 변수는 새로 생기지 않고 기존의 것이 계속 공유되고 있기 때문에 afterEach()가 필요한 거 아닐까 싶은데 제가 정확히 이해한 게 맞는지, 제 생각에 오류가 있는지 궁금합니다.

  • java
  • spring
  • mvc
  • spring-boot
11 1 댓글 2 좋아요 3 조회수 598

폐쇄망에서 package 추가

미해결

따라하며 배우는 리액트 A-Z[19버전 반영]

구글링을 해서 폐쇄망에 react 프로젝트를 어찌어찌 설치를 했습니다. 정상적으로 실행도 되고 프로그래밍도 가능합니다. 근데, 중간에 패키지를 추가하려고 인터넷이 가능한 곳에서 패키지를 추가해서 옮겨보려 했으나 잘 안되더군요. 폐쇄망에서 패키지 추가는 어떻게 해야 하는지 도움 부탁드립니다.

  • react
  • redux
  • tdd
  • typescript
  • next.js
  • 소프트웨어-테스트
  • 오프라인
  • 폐쇄망
metlin 댓글 1 좋아요 0 조회수 1166

문제로 남겨주신 uuid에 대한 코드는 없는건가요 ?

미해결

Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트

UUID에 대해 제가 작성한 로직의 방향이 맞는지 확인해 보고싶은데, 문제관련 코드는 어디에 있나요 ?

  • spring
  • tdd
  • jpa
  • 소프트웨어-테스트
  • unittest
라부베리laveberry 댓글 2 좋아요 0 조회수 900

Typeorm error: Table already exist

해결됨

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

Entity를 변경하고 나서 서버 재 시동 시 Connection Error, Table {Table_name} already exist 에러가 발생하고, Entity와 상관없이 소스를 수정해도 여전히 동일한 에러가 발생합니다. 에러를 찾아봤는데도 명확한 해답을 찾지 못해 질문 드립니다. [Nest] 1860 - 2023. 09. 14. 오후 10:13:42 ERROR [TypeOrmModule] Unable to connect to the database. Retrying (5)... QueryFailedError: Table 'tb_user_info' already exists 변경 전 @Column() @Field(() => String) USR_USER_TYPE: boolean 변경 후 @Column() @Field(() => Boolean) USR_USER_TYPE: boolean 현재 Package는 아래와 같고요. "@nestjs/typeorm": "^10.0.0", "typeorm": "^0.3.17" @Entity를 중복 선언하거나 소스가 두개이진 않습니다. (기존에도 똑같은 질문이 있어서 확인했습니다.) 다만 Table명을 다르게 하기 위해 아래처럼 했습니다. @Entity('TB_USER_INFO') @ObjectType() export class UserInfoEntity { ..... 이 에러를 찾아보니... @Entity({ name: 'TB_USER_INFO', schema: 'user_schema'}) 처럼 하라고 하는데.... 우선 제대로 된 해결법이 맞는지 궁금하고요. 만약 맞다면 저기서 얘기하는 schema가 무엇을 말하는지 못찾았습니다. ( 혹시 schema명이 database name이 아닌가 싶네요... 그렇게 했을때 별도로 다른 에러가 발생하진 않았거든요) 그리고 어떤 곳에선 synchronize를 false로 하고 직접 수정하는게 낫다는 사람들도 있더라고요. 조금 이해가 안되긴 하지만..... 해결됐습니다. ============================================================= 이런저런 테스트를 하다가 Stack overflow에서 어떤 사람이 @Entity작성 시 이름을 별도로 지정하지 않고 class명을 그대로 써서 해결됐다고 하길래 저도 그렇게 했더니 해결이 됐습니다. 최초 class명과 table명을 다르게 쓴 이유는 해당 entity클래스가 entity 라는걸 알아보기 쉽게 하려고 class에 entity라는 이름을 붙이고, table명에서는 entity를 쓰지 않았었습니다. 지금도 그렇게 하는 것이 더 편하다 생각하는데... 우선 에러를 해결하기 위해서는 이렇게 밖에 방법이 없네요;;;;; 혹시 테이블 명을 별도로 사용하고 에러가 나게 하지 않을 방법이 있을까요?

  • javascript
  • node.js
  • express
  • docker
  • tdd
  • rest-api
  • nestjs
yisi 댓글 2 좋아요 0 조회수 944

궁금한점 있습니다

미해결

자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

안녕하세요 강사님 궁금한것이 있어서 질문 남깁니다 public void returnBook(String bookName) { UserLoanHistory targetHistory = this.userLoanHistories.stream () .filter(history -> history.getBookName().equals(bookName)) .findFirst() .orElseThrow(IllegalAccessError::new); //findFist()는 옵셔널로 반홚나다 targetHistory.doReturn(); } 이 로직에서 만약 한 책을 빌렸다가 반납하면 그 히스토리를 반납완료 상태로 만들어주고 끝내고 다시 그 책을 빌리려고 한다면 새로운 히스토리를 만들어서 List에 저장할텐데 그럼 다시 그책을 반납할 경우 findFirst()를 해서 그 히스토리를 찾아왔을때 이미 반납된 책을 다시 반납하는 행위가 되는것이 아닌건지 궁금해서 질문 드립니다!

  • java
  • spring
  • aws
  • mysql
  • spring-boot
  • jpa
wjddjs21 댓글 1 좋아요 2 조회수 271

인기 태그

인프런 TOP Writers

주간 인기글