게시글
블로그
전체 172024. 05. 14.
0
[인프런 워밍업 클럽 1기] BE 6일차 과제
문제 1과제 #4에서 만들었던 API를 Controller - Service - Repository로 분리하기4일차 과제 내용: https://www.inflearn.com/blogs/6676Controller@RequiredArgsConstructor @RestController @RequestMapping("/api/v1/fruit") public class FruitController { private final FruitService fruitService; @PostMapping public ResponseEntity savedFruit(@RequestBody FruitRequest request) { fruitService.save(request); return ResponseEntity.ok().build(); } @PutMapping public ResponseEntity FruitStateUpdate(@RequestBody FruitRequest request) { fruitService.stateUpdate(request); return ResponseEntity.ok().build(); } @GetMapping("/stat") public ResponseEntity isSaleStateTotalPrice(@RequestParam String name) { return ResponseEntity.ok(fruitService.getSum(name)); } }Service@RequiredArgsConstructor @Service public class FruitService { private final FruitRepository fruitRepository; public void save(FruitRequest request) { fruitRepository.saveFruit(request); } public void stateUpdate(FruitRequest request) { boolean isFruitNotExist = fruitRepository.isFruitNotExist(request.getId()); if (isFruitNotExist) { throw new IllegalStateException("일치하는 과일 정보가 없습니다."); } fruitRepository.updateFruit(request.getId()); } public FruitTotalPriceResponse getSum(String name) { boolean isFruitNotExist = fruitRepository.isFruitNotExist(name); if (isFruitNotExist) { throw new IllegalStateException("일치하는 과일 정보가 없습니다."); } List sumResult = fruitRepository.getSum(name); long salesAmount = 0; long noSalesAmount = 0; for (FruitTotalPriceResponse result : sumResult) { salesAmount += result.getSalesAmount(); noSalesAmount += result.getNoSalesAmount(); } return new FruitTotalPriceResponse(salesAmount, noSalesAmount); } } Repository @RequiredArgsConstructor @Repository public class FruitRepository { private final JdbcTemplate jdbcTemplate; public void saveFruit(FruitRequest request) { String sql = "insert into fruit (name, warehousingDate, price) values (?, ? ,?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); } public void updateFruit(long id) { String sql = "update fruit set is_sale = 1 where id = ?"; jdbcTemplate.update(sql, id); } public List getSum(String name) { String sql = "select is_sale, sum(price) as totalPrice from fruit " + "where name = ? group by is_sale"; return jdbcTemplate.query(sql, new Object[]{name}, (rs, rowNum) -> new FruitTotalPriceResponse( rs.getBoolean("is_sale") ? rs.getLong("totalPrice") : 0, !rs.getBoolean("is_sale") ? rs.getLong("totalPrice") : 0)); } public boolean isFruitNotExist(long id) { String sql = "select * from fruit where id = ?"; return jdbcTemplate.query(sql, (rs, rowNum) -> 0, id).isEmpty(); } public boolean isFruitNotExist(String name) { String sql = "select * from fruit where name = ?"; return jdbcTemplate.query(sql, (rs, rowNum) -> 0, name).isEmpty(); } } 문제 2 FruitRepository를 FruitMemoryRepository 와 FruitMemoryRepository 로 나누자 먼저 FruitRepository를 만든다public interface FruitRepository { public void saveFruit(FruitRequest request); public void updateFruit(long id); public List getSum(String name); public boolean isFruitNotExist(long id); public boolean isFruitNotExist(String name); }FruitMemoryRepository@Primary @Repository public class FruitMemoryRepository implements FruitRepository{ private final List fruits = new ArrayList(); @Override public void saveFruit(FruitRequest request) { fruits.add(request); } @Override public void updateFruit(long id) { fruits.stream() .filter(fruit -> fruit.getId() == id) .findFirst() .ifPresent(fruit -> fruit.setSale(true)); } @Override public List getSum(String name) { return fruits.stream() .filter(fruit -> fruit.getName().equals(name)) .collect(Collectors.groupingBy(FruitRequest::isSale, Collectors.summingLong(FruitRequest::getPrice))) .entrySet().stream() .map(entry -> new FruitTotalPriceResponse( entry.getKey() ? entry.getValue() : 0, !entry.getKey() ? entry.getValue() : 0)) .collect(Collectors.toList()); } @Override public boolean isFruitNotExist(long id) { return fruits.stream().noneMatch(fruit -> fruit.getId() == id); } @Override public boolean isFruitNotExist(String name) { return fruits.stream().noneMatch(fruit -> fruit.getName().equals(name)); } } FruitMySqlRepository@Qualifier("mysql") @RequiredArgsConstructor @Repository public class FruitMySqlRepository implements FruitRepository { private final JdbcTemplate jdbcTemplate; public void saveFruit(FruitRequest request) { String sql = "insert into fruit (name, warehousingDate, price) values (?, ? ,?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); } public void updateFruit(long id) { String sql = "update fruit set is_sale = 1 where id = ?"; jdbcTemplate.update(sql, id); } public List getSum(String name) { String sql = "select is_sale, sum(price) as totalPrice from fruit " + "where name = ? group by is_sale"; return jdbcTemplate.query(sql, new Object[]{name}, (rs, rowNum) -> new FruitTotalPriceResponse( rs.getBoolean("is_sale") ? rs.getLong("totalPrice") : 0, !rs.getBoolean("is_sale") ? rs.getLong("totalPrice") : 0)); } public boolean isFruitNotExist(long id) { String sql = "select * from fruit where id = ?"; return jdbcTemplate.query(sql, (rs, rowNum) -> 0, id).isEmpty(); } public boolean isFruitNotExist(String name) { String sql = "select * from fruit where name = ?"; return jdbcTemplate.query(sql, (rs, rowNum) -> 0, name).isEmpty(); } } FruitService 부분에 생성자에 @Qualifier("mysql")가 있기 때문에 FruitMemoryRepository에 @Primary 어노테이션이 있더라도 직접 지정한 클래스가 우선순위가 높기 때문에 FruitMemoryRepository 주입 받는다. public FruitService(@Qualifier("mysql")FruitRepository fruitRepository) { this.fruitRepository = fruitRepository; }
인프런워밍업클럽
・
스터디1기
・
백엔드
2024. 05. 08.
0
[인프런 워밍업 클럽 1기] BE 5일차 과제
문제주어지는숫자를 하나를 받고 해당 숫자만큼 주사위를 돌려, 각 숫자가 몇 번 나오는지 출력하는 문제 public class Main { public static void main(String[] args) { System.out.println("주사위 면의 수를 입력하세요:"); Scanner scanner = new Scanner(System.in); int a = scanner.nextInt(); int r1 = 0, r2 = 0, r3 = 0, r4 =0, r5 = 0, r6 = 0; for (int i = 0; i = 0 && b = 1 && b = 2 && b = 3 && b = 4 && b = 5 && b 제시된 코드를 최대한 클린하게 만들어라 public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int rolls = inputDice(scanner); int sides = inputSide(scanner); int[] results = rollTheDice(rolls, sides); resultPrint(sides, results); } private static int inputSide(Scanner scanner) { System.out.print("주사위 면를 입력해주세요 :"); int sides = scanner.nextInt(); return sides; } private static int inputDice(Scanner scanner) { System.out.print("던질 횟수를 입력해주세요 :"); int rolls = scanner.nextInt(); return rolls; } private static int[] rollTheDice(int rolls, int sides) { int[] results = new int[sides + 1]; for (int i = 1; i 주사위 면을 받을 수 있는 inputSide함수와 몇번 주사위를 굴릴건지 입력을 받을 수 있는 inputDice 함수,주사위를 돌려 결과를 저장하는 rollTheDice함수와 결과를 출력하는 resultPrint 함수로 구성했다.각 변수는 의미있는 변수명으로 바꾸고 메서드명도 그에 맞게 바꾸었다.
인프런워밍업클럽
・
스터디1기
・
백엔드
2024. 05. 07.
0
[인프런 워밍업 클럽 1기] BE 4일차 과제
문제 1하기전에 테이블을 작성을 합니다.create table fruit ( id bigint auto_increment, name varchar(20), warehousingDate date, price bigint, is_sale boolean default 0, primary key (id) ); 과일 정보 저장하는 API요청을 받기 위해 FruitRequest 를 만듭니다.@Data public class FruitRequest { private String name; private LocalDate warehousingDate; private long price; } 요청을 받고 쿼리문을 날려 저장하는 비지니스 로직을 만듭니다.@AllArgsConstructor @Service public class FruitService { private final JdbcTemplate jdbcTemplate; public void save(FruitRequest request) { String sql = "insert into fruit (name, warehousingDate, price) " + "values (?, ? ,?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); } }FruitService 클래스를 만들어 데이터 삽입sql을 만들고 jdbcTemplate로 업데이트 해주는 비지니스 로직을 만듭니다. @AllArgsConstructor @RestController @RequestMapping("/api/v1/fruit") public class FruitController { private final FruitService fruitService; @PostMapping public ResponseEntity savedFruit(@RequestBody FruitRequest request) { fruitService.save(request); return ResponseEntity.ok().build(); } } FruitController를 만들어 비지니스 처리를 완료하면 정상 응답을 내리는 메서드를 만들면 됩니다. int 와 long 사이에 long을 사용하는 이유는 표현 범위 때문입니다.long 이 int 보다 월등히 많은 수를 표현하기 때문입니다. 문제 2과일 팔리면 과일 정보 기록 API응답을 받기 위해 FruitStateRequest 클래스를 만듭니다.@Data public class FruitStateRequest { private Long id; }비즈니스 로직을 수행하기 위해 FruitService 크랠스에서 다음과 같은 메서드를 작성합니다.public void stateUpdate(FruitStateRequest request) { String fruitSql = "select * from fruit where id = ?"; boolean isFruitNotExist = jdbcTemplate .query(fruitSql, (rs, rowNum) -> 0, request.getId()).isEmpty(); if (isFruitNotExist) { throw new IllegalStateException("일치하는 과일 정보가 없습니다."); } String sql = "update fruit set is_sale = 1 where id = ?"; jdbcTemplate.update(sql, request.getId()); }먼저 해당 id로 과일 정보가 있는지 확인을 합니다.없으면 일치하는 과일 정보가 없다고 경고를 주고 있으면 업데이트 sql문으로 업데이트 쿼리를 날립니다.@PutMapping public ResponseEntity FruitStateUpdate(@RequestBody FruitStateRequest request) { fruitService.stateUpdate(request); return ResponseEntity.ok().build(); }FruitController에 put요청을 받기 위한 메서드를 만들고 정상으로 수행이 되면 200 요청을 보냅니다.문제 3특정 과일 기준으로 팔린 금액과 팔리지 않은 금액 조회먼저 수행하기전 테이블에 과일 정보를 입력해둡니다.FruitController 에 과일 이름 파라미터를 받고 처리하기 위해 메서드를 만듭니다.@GetMapping("/stat") public ResponseEntity isSaleStateTotalPrice(@RequestParam String name) { return ResponseEntity.ok(fruitService.getSum(name)); }서비스 로직에서 정상 수행을 하면 결과값을 반환하게 만들었습니다. 이제 비지니스로직을 FruitService에 작성해줍시다public FruitTotalPriceResponse getSum(String name) { String fruitSql = "select * from fruit where name = ?"; boolean isFruitNotExist = jdbcTemplate .query(fruitSql, (rs, rowNum) -> 0, name).isEmpty(); if (isFruitNotExist) { throw new IllegalStateException("일치하는 과일 정보가 없습니다."); } String sql = "select is_sale, sum(price) as totalPrice from fruit " + "where name = ? group by is_sale"; List results = jdbcTemplate.query(sql, new Object[]{name}, (rs, rowNum) -> new FruitTotalPriceResponse( rs.getBoolean("is_sale") ? rs.getLong("totalPrice") : 0, !rs.getBoolean("is_sale") ? rs.getLong("totalPrice") : 0)); long salesAmount = 0; long noSalesAmount = 0; for (FruitTotalPriceResponse result : results) { salesAmount += result.getSalesAmount(); noSalesAmount += result.getNoSalesAmount(); } return new FruitTotalPriceResponse(salesAmount, noSalesAmount);과일 이름으로 과일 정보가 있는지 확인합니다.없으면 에러를 일으키고 있다면 다음 로직을 수행합니다.과일이 팔렷는지 아닌지 확인을 위해 is_sale 그룹으로 해당 과일의 is_sale와 price 합계를 구합니다.그리고 쿼리문으로 리스트 결과를 받아 각각의 합계를 구한후 리턴합니다.매번 repository로 하던 습관으로 하다보니 간만에 jdbcTemplate 으로 하는데 꽤나 예를 먹은 과제인거 같다.
인프런워밍업클럽
・
스터디클럽1기
・
백엔드
2024. 05. 03.
0
[인프런 워밍업 클럽 1기] BE 3일차 과제
자바의 람다식은 왜 등장했을까? 자바는 람다식 함수형 프로그램밍이 사용한 이유는 불필요한 코드를 줄이고, 가독성을 높이기 위해서다 .그리고 함수 만드는 과정없이 한번에 처리 할 수 있기 때문에 생산성이 높아진다.병령 프로그램밍에 용이하다. 람다식과 익명 클래스는 어떤 관계가 있을까? 함수형 프로그래밍이란 함수를 정의하고 이 함수를 데이터 처리부로 보내 데이터를 처리하는 기법이다.데이터 처리부는 데이터만 가지고 있을 뿐, 처리 방법이 정해져 있지 않아 외부에서 제공져 있지 않아 외부에서 제공된 함수에 의존한다.데이터 처리부는 제공된 함수의 입력값으로 데이터를 넣어 함수에 정의된 처리 내용을 실행하고, 동일한 데이터라도 함수 A를 제공한 결과 값과 함수 B를 제공하여 처리된 결과 값은 다를 수 있다 이것이 함수형 프로그램의 특징인 데이터 처이의 다형성이다.람다식은 함수를 하나의 식으로 표현하여 익명 함수를 반환한다.익명 클래스는 선언된 클래스 내에서만 한 번만사용될 경우 별도로 변수에 담을 필요가 없다.람다식을 쓰면 함수형 인터페이스로 인스턴스를 만들 수 있으며 코드를 줄 일 수 있다.메서드 매개변수와 리턴 타입, ㅌ변수로 만들어 사용도 가능하다. 람다식의 문법은 어떻게 될까? 데이터 처리부에 제공되는 함수 역활 하는 매개변수를 가진 중괄호 블록이다.{매개변수, ...} -> {처리 내용}; 인터페이스가 단 하나의 추상 메소드를 가질 경우 이를 함수형 인터페이스라고 한다.public interface Runnable { void run(); }람다식() -> { ...}; @FunctionalInterface public interface Calculable { void calculate(int x, int y); }람다식(x, y ) -> { ...}@FunctionalInterface 어노테이션을 사용한 이유는 인터페이스가 함수형 인터페이스를 보장하기 위해서다.붙이는 것은 선택사항이지만, 컴파일 과정에서 추상 메소드가 하나인 검사하기 때문에 정확한 함수형 인터페이스를 작성하게 도와주는 역활을 해준다.매 매개변수가 없는 람다식실행문이 하나일 경우 중괄호를 생략 가능하고 두개 이상일 경우는 생략할 수 없다.() -> 실행문; () -> { 실행문; 실행문; } 매개변수가 있는 람다식매개변수를 선언할 때 타입은 생략할 수 있고, 구체적인 타입 대신에 var를 사용할 수 있다.(타입, 매변수, ... ) -> { 실행문; 실행문; }(타입, 매변수, ... ) -> 실행문;(var 매개변수, ...) -> { 실행문; 실행문; }(var 매개변수, ...) -> 실행문;(매개변수, ...) -> { 실행문; 실행문; }(매개변수, ...) ->실행문;매개변수 -> { 실행문; 실행문 };매개변수 -> 실행문; 리턴값이 있는 람다식return 문 하나만 있는 경우에는 중괄호와 함께 return 키워드를 생략가능하다.(매개변수, ...) -> { 실행문; return 값 )(매개변수, ...) -> 값 메소드 참조메소드를 참조하여 매개변수의 정보 및 리턴 타입을 알아내 람다식에서 불필요한 매개변수를 제거한다. (left, right) -> Math.max(left, right);Math.max() 메소드의 매개값은 전달하는 역활만 하기 때문에 다음과 같이 생략이 가능하다.Math :: max;정적 메소드를 참조할 경우에는 클래스 이름 뒤에 :: 기호를 붙이고 메소드 이름을 기술한다.클래스 :: 메서드참조변수 :: 메소드 매개변수의 메소드 참조(a, b) -> {a.instaceMethod(b);}메소드 참조는 a 클래스 이름 뒤에 :: 기호를 붙이고 매소드 이름을 기술한다.작성 방법은 메소드 참조와 동일하지만, a의 인스턴스 메소드가 사용된다는 점이 다르다.클래스 :: instaceMethod Reference이것은 자바다(책)
인프런워밍업클럽
・
스터디1기
・
백엔드
2024. 05. 03.
0
[인프런 워밍업 스터디 클럽] 1기 백엔드
문제 1두 수를 입력하면, 다음과 같은 결과가 나오는 GET API만들기@GetMapping("api/v1/calc") public ResponseEntity calc(Calculator request){ CalculatorResponse response = calculatorService.calc(request); return ResponseEntity.ok(response); }CalculatorController에 Post 요청 메서드로 외부 파라미터를 받기 위해 응답 객체로 Calculator 를 정의했습니다. @Getter @AllArgsConstructor public class Calculator { private final int num1; private final int num2; } calulatorService 정의하여 비지니스 로직을 작성했습니다.@Service public class CalculatorService { public CalculatorResponse calc(Calculator request) { return new CalculatorResponse( add(request), minus(request), multiply(request)); } private static int add(Calculator request) { return request.getNum1() + request.getNum2(); } private static int minus(Calculator request) { return request.getNum1() - request.getNum2(); } private static int multiply(Calculator request) { return request.getNum1() * request.getNum2(); } }응답용 객체로 CalculatorResponse를 정의를 했습니다.@Data @NoArgsConstructor @AllArgsConstructor public class CalculatorResponse { private int add; private int minus; private int multiply; }그리하여 calulatorService에 요청 받은 두 데이터를 처리하여 CalculatorResponse로 ResponseEntity에 담아 200 응답으로 JSON 반환값으로 보냅니다.문제 2날짜를 입력하면, 몇 요일인지 알려주는 GET API 만들기@RestController @RequestMapping("/api/v1") public class DateController { @GetMapping("/getDayOfWeek") public ResponseEntity getDayOfWeek(@RequestParam String date) { try { LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd")); String dayOfTheWeek = localDate.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.ENGLISH).toUpperCase(); return ResponseEntity.ok(Collections.singletonMap("dayOfTheWeek", dayOfTheWeek)); } catch (DateTimeException e) { return ResponseEntity.badRequest().body("Invalid date format"); } } }클라이언트 요청을 처리하고 응답을 반환하기 위해 @RestController 어노테이션을 씁니다.@GetMapping("/getDayOfWeek") 어노테이션을 써서 GET 요청이 오면 메소드가 호출됩니다.@RequestParam을 써서 date 파리미터를 받습니다.받은 날짜 문자열을 'LocalDate' 객체로 반환하기 위해 'DateTimeForrmatter'를 사용하여 날짜 형식을 지정합니다. 그리고 LocalDate 객체의 getDayofWeek() 메서드를 사용하여 요일을 구할 수 있습니다.마지막으로 JSON 형식으로 반환하기 위해 Collections.singletonMap을 사용하여 간단하게 키-값 쌍을 만들어 JSON형식으로 반환할 수 있습니다.문제3여러 수를 받아 총 합을 반환하는 POST API 만들기 @Data public class CalculatorRequest { private List numbers; }클라이언트로 부터 받기 위해 숫자 배열릉 받기 위해 List 필드를 가진 클래스를 만듭니다. @PostMapping("/sum") public int calculateSum(@RequestBody CalculatorRequest request) { return request.getNumbers().stream() .mapToInt(Integer::intValue) .sum(); }CalculatorController에 Post요청을 처리하기 위해 @PostMapping 어노테이션을 사용합니다.@@RequestBody를 사용하여 JSON 데이터를 CalculatorRequest 객체로 변환됩니다.stream을 돌려 합계를 계산한 다음, 그 결과를 반환합니다.
인프런워밍업클럽
・
스터디1기
・
백엔드
2024. 04. 29.
0
[인프런 워밍업 스터디 클럽] 1기 백엔드 2일차 과제
어노테이션 사용하는 이유어노테이션은 사용 용도로 3가지가 있습니다.1. 컴파일 시 사용하는 정보 전달2. 빌드 툴이 코드를 자동으로 생성할 때 사용하는 정보 전달3.실행 시 특정 기능을 처리할 때 사용하는 정보 전달컴파일 시 사용하는 정보 전달의 대표적인 예는 @Override 어노테이션입니다.컴파일러가 메소드 재정의 검사를하도록 설정합니다. 재정의되지 않았다면 컴파일러는 에러를 발생시킵니다.웹 개발에 많이 사용하는 Spring Framework 또는 Spring Boot는 다양한 종류의 어노테이션을 사용해서 웹 애플리케이션을 설정하는데 사용합니다.나만의 어노테이션은 어떻게 만들 수 있을까? 어노테이션을 정의하는 방법은 인터페이스를 정의하는것과 유사합니다.@interface 뒤에 사용할 어노테이션을 이름을 정의합니다.오노테이션은 속성을 가질 수 있으며, 속성은 타입과 이름으로 구성됩니다. 속성은 기본값 default 키워드로 지정할 수 있습니다.어떤 대상에 설정 정보를 적용할 것인지, 적용대상을 정의 해야 합니다.클래스 명위에 @Target 어노테이션을 붙어 정의 합니다.적용할 수 있는 대상의 종류는 ElememtType 열거 상수로 정의되어 있습니다.TYPE : 클래스, 인터페이스 열거타입ANOTATION_TYPE: 어노테이션FIELD: 필드CONSTERUCTOR: 생성자METHOD: 메서드LOCAL_VARIABLE: 로컬 변수 예시어노테이션 정의@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PrintAnnotation { String value() default "-"; int number() default 15; } 서비스 어노테이션 적용public class Service { @PrintAnnotation public void method1() { System.out.println("실행 내용1"); } @PrintAnnotation("*") public void method2() { System.out.println("실행 내용2"); } @PrintAnnotation(value = "#", number = 20) public void method3() { System.out.println("실행 내용3"); } } 실행 코드public class PrintAnnotationExample { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { Method[] declaredMethods = Service.class.getDeclaredMethods(); for (Method method : declaredMethods) { PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class); printList(printAnnotation); method.invoke(new Service()); printList(printAnnotation); } } public static void printList(PrintAnnotation printAnnotation) { if (printAnnotation != null) { int number = printAnnotation.number(); for (int i = 0; i 출력 내용
인프런워밍업클럽
・
스터디1기
・
백엔드
2024. 03. 19.
3
인프런 워밍업 클럽 후기
독학도 하면서 부트캠프 하는 와중에, 뭔가 새로운거에 도전하고 싶었습니다. 그와중에 인프런에서 워밍업 클럽이라는것을 한다고 하기에 참여하기로 결정을 했습니다.결론적으로 말하자면 뜻밖에 좋은 스터디 공부였다고 생각합니다.스터디 하기 전에 강의는 옛날에 다한 강의 였지만, 진도표를 보고 다시 복습하고 그 내용에 맞는 과제를 하면서 내가 무엇이 부족하고 그 동안 공부한것을 정리하는 시간을 가지게 되어 좋은 개발자로 한발자국을 내딛을 거라 생각합니다. 또한, 무엇보다 중간 Q&A 한 다는 것을 까먹어서 처음거는 놓치고 두번째 부터 참여했습니다.온라인 라이브 세션에서 코드 리뷰나 질문에 대해 자세히 설명해주셔서 배울게 너무 많구나 생각하면서 유익한 시간을 보낸거 같습니다.물론 강의랑 과제는 다했으나 완주 러너에 대상이 안되었지만, 이런 스터디는 처음 참가하는거라 신선했으며 좋은 경험을 얻었다고 생각합니다. 여러 사람들의 코드를 보면서 내 코드와 비교하는것도 좋은 경험인거 같습니다.수료식은 온라인으로 했습니다. 인프런 참여 행사는 거진 판교인거 같아서 시간도 너무 저녁이고 멀기에 가지 못 한게 아쉽네요. 시간적으로 낮이나 장소를 홍대나 서울 안쪽에서 했으면 갈 수 있을거 같은데 항샹 판교에 하신거 같아 참여하기 힘든거 같습니다. 결과적으로, 인프런 워밍업 클럽에 참여한 것은 좋으 결정이었습니다. 그 동안 독학과 학교 공부와 부트 캠프로 해왔지만,거기서 얻지 못 한 경험이 있기 때문에 유익했습니다. 다른 분들이 적극적으로 하시는 분들도 계시고 물론 저는 소극적인 성격때문에 잘 못한게 문제였지만, 유익한 지식을 주신 코치님들과 운영을 영실히 하려는 인프런분들과 여러 수강생분들이 있어 뜻깊은 시간을 보낸거 같습니다. 다음에도 기회가 있으면 참여하고 싶습니다.
인프런
・
인프런워밍업클럽
・
스터디0기
2024. 03. 10.
0
[인프런 워밍업 스터디 클럽 0기] 발자국 3주차
그동안 해온것들미니 프로젝트 4단계 배포 전까지는 완료 첫 날부터 준비하면서 매일 저녘부터 밤까지 해왔다.많은 고민을 하고 코드 치다 지우고 반복을 했지만, 이번주 중간점검 라이브에서 다른 사람거 코드 리뷰를 보면서 내가 배울게 많다고 느꼈다.공부는 끝이 없으며 배울게 너무 많은것 같다.특히 느낀게 문제 해결을 위해 기술을 여러개 있지만, 사람마다 해결 방법이 달라 어느쪽이 좋은지는 의견이 다르는점그렇다면 어느 것을 선택하는게 좋을지는 각 기술의 장단점을 아는게 좋은것 같다.무엇보다 회사에서 결정해줄거 같지만, 아직 취업 준비생이다 보니 모르는것보다 아는것이 나을것 같다. 일급 컬렉션이나 무조건 JPA에 의존하는게 아니라는 점등 배울게 너무 많았다.그동안 3주 길다면 길고 짧다면 짧은 기간이지만, 간만에 재밌었고, 배울게 많은 시간이었다.다음에도 다시 모집한다면, 다시 참여할 생각이다.
워밍업클럽
2024. 03. 03.
0
[인프런 워밍업 스터디 클럽 0기] 발자국 2주차
2주차 학습 내용 Day 6: 스프링 컨테이너란과 사용법스프링 컨테어니러를 통해 다양한 설정들을 모두 자동으로 해주며, 클래스가 들어갈 때는 빈을 식별할 수 있게 이름 및 타입과 함께 다양한 정보를 저장하며 자동으로 의존성을 주입해준다.빈 등록 방법에는 클래스에 맞는 빈 어노테이션을 붙이고 @Qualifer와 @Primary 사용법을 배웠다. Day 7: 왜 Spring Data JPA를 사용하는가?지금까지 sql쿼리문을 짜서 하는것은 너무 힘들다. 문자열을 작성하기 때문에 실수를 인지하는 시점도 느리며, 특정 데이터베이스에 종속적이다. 반복 잔업이 많아지며 테이블과 객체 간에 패러다임이 다르다는 문제가 있었다.그래서 나온게 JPA다. 데이터를 영국적으로 보관하기 위해 Java 진영에서 정해진 규칙이다.프로젝트를 함계하면서 사용법을 익혔다. Day 8: 트랜잭션트랜잭션이란 쪼갤 수 없는 업무의 최소 단위 의미이다.쉽게 말하자면 여러 SQL을 사용시 한 번에 성공시키거나 , 하나라도 실패하면 모두 실패하는 기능이다.트랜잭션을 왜 사용하는지 배웠다. 학교나 자격증때 공부했지만 다시 복습하니 왜 트랜잭션이 중요하지 다시 알게된 계기가 되었다. Day 9: 책 요구사항 구현제시된 API 명세서를 보고 어떻게 구현하는지 실제 프로젝트를 개발하면서 배웠다.명세서를 보고 어떻게 개발을 해야할지 아직 미숙했던 나에게 복습하게 되어 더 하나 더 배워가던 시간이다. Day 10: 객체지향과 JPA연관 관계연관 관계를 써서 좀더 객체지향적 개발방법과 연관관계의 이해와 다양한 옵션, 도메인에 맞쳐진 로직을 넣어 서비스 코드에 의존성을 낮추는 방법을 배웠다. 미션사실 미리미리 하나씩 해왔다. 미션들을 강의에 나온거에 플러스 알파 내용이라 강의만에 그치지 않고 생각을 하면서 이렇게 좋았을까 여러 생각을 하면서 해왔다. 결국 다 풀긴했지만, 더 좋은 방법이 있을거 같긴하다. 미니 프로젝트:첫날부터 프로젝트는 시작했다. 명세된 API 명세서 말고도 생각을 많이 했다.직원등록 할 때 팀등록 없이 하고 나중에 팀을 등록 하는 로직이나 팀이 없는데 직원 등록시 팀이름이 있으면 어떻게 해야할 지 많은 고민을 한거 같다. 그리고 프로젝트는 거의 마무리 작업을 하고 있다자세한 내용은 다음주 발자취에는 프로젝트에 대해 다룰것이다.
인프런워밍업클럽
2024. 02. 26.
0
[인프런 워밍업 클럽 0기] BE 7일차 과제
문제1JPA 적용하기 FruitRepository에 JpaRepository 상속및 함수 작성public interface FruitRepository extends JpaRepository { boolean existsByName(String name); @Query("SELECT new com.group.libraryapp.dto.fruit.response.FruitTotalPriceResponse(" + "SUM(CASE WHEN f.isSale = true THEN f.price ELSE 0 END), " + "SUM(CASE WHEN f.isSale = false THEN f.price ELSE 0 END)) " + "FROM Fruit f WHERE f.name = :name GROUP BY f.isSale") List getSum(String name); } Fruit 엔티티 작성@Getter @AllArgsConstructor @NoArgsConstructor @Builder @Entity public class Fruit { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; private LocalDate warehousingDate; private long price; @Column(name = "is_sale") private boolean isSale; }FruitService 수정public void save(FruitRequest request) { fruitRepository.save(new Fruit().builder() .name(request.getName()) .warehousingDate(request.getWarehousingDate()) .price(request.getPrice()) .build()); } public void stateUpdate(FruitRequest request) { Fruit fruit = fruitRepository.findById(request.getId()) .orElseThrow(() -> new EntityNotFoundException("일치하는 과일 정보가 없습니다.")); fruit.builder() .isSale(true) .build(); } public FruitTotalPriceResponse getSum(String name) { boolean isFruitExist = fruitRepository.existsByName(name); if (!isFruitExist) { throw new IllegalStateException("일치하는 과일 정보가 없습니다."); } List sumResult = fruitRepository.getSum(name); long salesAmount = 0; long noSalesAmount = 0; for (FruitTotalPriceResponse result : sumResult) { salesAmount += result.getSalesAmount(); noSalesAmount += result.getNoSalesAmount(); } return new FruitTotalPriceResponse(salesAmount, noSalesAmount); }문제 2과일 이름을 받아 해당 과일 개수 반환하기 FruitController에 과일 이름을 받아 처리할 수 있도록 추가@GetMapping("/count") public ResponseEntity getCount(@RequestParam String name) { return ResponseEntity.ok(fruitService.getFruitCountByName(name)); } FruitService 클래스에 controler에서 받은 이름으로 repository로 결과 받아 처리 로직 추가public long getFruitCountByName(String name) { return fruitRepository.getFruitCountByName(name); }FruitRepository에서 JPQL로 쿼리문으로 작성하여 결과값을 반환했다.@Query("select count (f) from Fruit f where f.name = :name") long getFruitCountByName(String name); 문제 3아직 판매되지 않은 특정 과일 금액 이상 혹은 금액 이하 과일 목록 받아오기 응답을 주기위해 FruitResponse 만듭니다@AllArgsConstructor @NoArgsConstructor @Data public class FruitResponse { private String name; private long price; private LocalDate warehousingDate; } FruitController에 옵션과 가격을 받아 서비스 계층으로 전달하여 판매되지 않은 결과값을 반환 할 수 있게 추가합니다.@GetMapping("/list") public ResponseEntity> getFruits( @RequestParam String option, @RequestParam long price) { List fruits = fruitService.getFruitsByPrice(option, price); return ResponseEntity.ok(fruits); } FruitService에 옵션에 따라 금액을 이상으로 할지 이하로 할지 구분하여 해당 과일 리스트를 얻어 FruitResponse 리스트로 보냅니다.public List getFruitsByPrice(String option, long price) { List fruits; if (option.equals("GET")) { fruits = fruitRepository.findByPriceGreaterThanEqualAndIsSaleFalse(price); } else if (option.equals("LTE")) { fruits = fruitRepository.findByPriceLessThanEqualAndIsSaleFalse(price); }else throw new IllegalStateException("일치하는 옵션이 없습니다."); return fruits.stream() .map(Fruit::toDto) .collect(Collectors.toList()); }fruitRepository에 나오는 값은 Fruit 클래스이므로 FruitResponse 변환을 위해 Fruit클래스에 toDto메서드를 만듭니다.public FruitResponse toDto() { return new FruitResponse(name, price, warehousingDate); }FruitRepository에 금액 이상 혹은 이하 에 판매하지 않은 과일 목록을 구하기 위해 메서드를 추가합니다.List findByPriceGreaterThanEqualAndIsSaleFalse(long price); List findByPriceLessThanEqualAndIsSaleFalse(long price);응답 결과
워밍업클럽
・
과제