게시글
블로그
전체 52024. 02. 23.
1
[5일차] 인프런 워밍업 스터디 클럽 0기- BE
오늘의 문제는 위 코드를 클린 코드 관점에서 리팩토링하는 것입니다. 저는 아래와 같이 함수별로 하는 일을 나누었습니다.하나의 함수는 하나의 일만 해야 하니까요.import java.util.Scanner; /* * 1. 변수 및 메소드 이름 변경 : 코드의 가독성을 높임 * 2. 배열 사용 : 각 주사위 숫자에 대한 결과를 배열에 저장 * 3. 메소드 분리 : 주사위 던지기와 결과로 메소드를 분리 * */ public class MainClean { public static void main(String[] args) { System.out.println("던질 주사위 횟수를 입력하세요: "); Scanner scanner = new Scanner(System.in); int numberOfThrows = scanner.nextInt(); int[] throwResults = new int[6]; simulateDiceThrows(numberOfThrows, throwResults); printResults(throwResults); } private static void simulateDiceThrows(int numberOfThrows, int[] throwResults) { for (int i = 0; i 주사위의 시뮬레이션을 해주는 simulateDiceThrows 함수와 출력해주는 printResults 함수로 나누었습니다.클린코드 관점에서 입니다. 그런데 한번 더 생각을 해봅니다.첫번째 코드와 제가 만든 코드를 살펴 봅니다.저는 아무래도 첫번째 절차지향적인 코드가 더 가독성이 있고 더 유지보수가 쉬워 보입니다.그럼 코드의 복잡도가 올라가면 어떨까요?함수가 수백개로 잘게 쪼개 진다면? 추상화를 더 시켜서 더더욱 추상적으로 만든다면? 과연 누구든지 와서 유지보수 하기 쉬워질까요?저는 잘 모르겠습니다.제 코드가 잘 못 된 것 같습니다. 더 좋은 코드가 무엇인지 도저히 모르겠습니다.좀 도와주세요ㅠㅠㅠ
백엔드
2024. 02. 22.
1
[4일차] 인프런 워밍업 스터디 클럽 0기- BE
오늘도 API 문제였습니다. 저는 사실 API 문제가 재미있습니다 :)문제 1 localhost:8080/api/v2/fruit을 POST 방식으로 요청하면 200이 나오게 하라아래와 같이 보내면 "정상 처리 되었습니다."가 나오도록 만들었습니다.{ "name":"사과", "warehousingDate":"2024-02-01", "price":4000 }코드는 아래와 같고 저는 메모리에 저장하였습니다~@RequestMapping("/api/v1") @RestController public class FruitController { private List fruits = new ArrayList(); private long makeId= 0L; @PostMapping("/fruit") public ResponseEntity fruitCreate(@RequestBody FruitRequest request) { fruits.add(new Fruit(makeId++, request.getName(), request.getWarehousingDate(), request.getPrice(), false)); return ResponseEntity.ok("정상 처리 되었습니다."); }메모리에 저장하는 과정에서 저는 Fruit을 아래와 같이 설정하였습니다.왜냐하면 판매 프라이머리키 값과 판매여부도 필요했기 때문입니다. 판매 여부는 간단하게 saleYn으로 하였습니다.그리고 id값은 제가 만든 서비스가 대박이 나서 int가 커버할 수 있는 범위를 넘어 섰을 경우를 대비해서 long으로 잡았습니다 :) @Getter public class Fruit { private final long id; private final String name; private final LocalDate warehousingDate; private final long price; private final Boolean saleYn; public Fruit(long id, String name, LocalDate warehousingDate, long price, Boolean saleYn) { this.id = id; this.name = name; this.warehousingDate = warehousingDate; this.price = price; this.saleYn = saleYn; } }이제 데이터가 잘 들어 갔는지 확인을 하기 위해 API를 하나 더 만들었습니다. 저는 실제 DB를 아직 사용하지 않았기 때문에 API로 확인하기로 하였습니다. 코드는 아주 간단합니다.@GetMapping("/fruits") public List fruits() { return fruits; }GET 방식으로 localhost:8080/api/v1/fruits 호출하면 아래와 같이 출력 됩니다. 잘 들어 갔습니다 🙂[ { "id": 0, "name": "사과", "warehousingDate": "2024-02-01", "price": 4000, "saleYn": false }, { "id": 1, "name": "귤", "warehousingDate": "2024-02-02", "price": 2000, "saleYn": false } ] 그럼 2번 문제로 넘어 가겠습니다.PUT 으로 localhost:8080/api/v1/fruit 아래 body를 보내면 팔린 정보를 기록하기로 하였습니다. 여기서 saleYn을 사용합니다.{ "id":1 }코드는 아래와 같습니다.이번 코드는 좀 복잡합니다. PUT으로 넘겨준 아이디와 현재 가지고 있는 아이디가 같으면 현재의 정보를 temp데이터로 담았다가 List를 지우고 다시 같은 값으로 담도록 처리하였습니다. 이 부분을 좀 더 깔끔하게 처리 하고 싶은데 더 이상 생각이 나지 않습니다ㅠ@PutMapping("/fruit") public ResponseEntity fruitDelete(@RequestBody FruitRequest request){ for (int i = 0; i 위 API를 보내면 아래와 같이 변경된 리스트를 볼 수 있습니다. id 1번의 saleYn이 true로 변경되었고 저는 이 값은 판매되었다고 사용합니다.[ { "id": 0, "name": "사과", "warehousingDate": "2024-02-01", "price": 4000, "saleYn": false }, { "id": 1, "name": "사과", "warehousingDate": "2024-02-01", "price": 4000, "saleYn": true } ] 드디어 문제 3번입니다!!!GET으로 localhost:8080/api/v1/fruit/stat 보내면 아래와 같이 출력되도록 하는 API 입니다.{ "salesAmount": 2000, "notSalesAmount": 4000 }아래는 코드입니다. 저는 stream으로 처리해 보았습니다 😀@GetMapping("/fruit/stat") public FruitSalesResponse sales() { long salesAmount = fruits.stream().filter(fruit-> fruit.getSaleYn().equals(true)).mapToLong(Fruit::getPrice).sum(); long notSalesAmount = fruits.stream().filter(fruit-> fruit.getSaleYn().equals(false)).mapToLong(Fruit::getPrice).sum(); return new FruitSalesResponse(salesAmount,notSalesAmount); }상세 코드는 깃헙을 참고해 주세요 ~ 더 좋은 코드가 있다면 공유 부탁드립니다!!!!
백엔드
2024. 02. 19.
0
[2일차] 인프런 워밍업 스터디 클럽 0기- BE
2일차는 API를 만드는 과제입니다.API란 내가 어떻게 보낼테니 너는 이렇게 받아줘 라는 일종의 약속입니다.어려운 말로는 Application Protocol Interface 입니다. 그냥 약속이라는 뜻입니다.문제1 두 수를 입력하면, 다음과 같은 결과가 나오는 GET API를 만들어 보자!localhost:8080/api/v1/operation?number1=10&number2=20위와 같이 입력하면 아래와 같이 출력되는 문제입니다.{ "add": 30, "minus": -10, "multiply": 200 }이 문제는 그렇게 어렵지 않았습니다. 핵심은 DTO 였습니다.저는 아래와 같이 구현하였습니다.생성자에서 바로 모든 로직을 구해 버렸죠.public class Operation { private final int add; private final int minus; private final int multiply; public Operation(OperationRequest request) { this.add = request.number1() + request.number2(); this.minus = request.number1() - request.number2(); this.multiply = request.number1() * request.number2(); } }위와 같이 구현하면 Controller는 아래와 같이 간단하게 구현하면 됩니다.@RestController @RequestMapping("/api/v1") public class OperationController { @GetMapping("/operation") public Operation operation(OperationRequest request) { return new Operation(request); } }이번 과제를 하면서 새롭게 알게된 것이 있습니다. 바로 record 입니다.DTO를 다 만들고 보니 인텔리제이가 인텔리하게 아래와 같이 변경해 주고 잘 돌아 가는 것을 확인하였습니다.public record OperationRequest(int number1, int number2) { }진짜 마법과 같은 일이었습니다. record에 대해서 조금 더 알아 봐야 할 것 같습니다. 문제2... 또 문제가 있었습니다. 너무 힘듭니다. 그렇지만 힘을 내 봅니다.localhost:8080/api/v1/day-of-the-week?date=2024-02-03위와 같이 입력하면 아래와 같이 출력되어야 합니다.{ "dayOfTheWeek": "SATURDAY" }이 문제는 살짝 친구(ChatGPT)에게 물어 봤습니다.@RequestMapping("/api/v1") @RestController public class DayOfTheWeekController { @GetMapping("/day-of-the-week") DayOfTheWeek dayOfTheWeek(@RequestParam("date") String date) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate dateFormat = LocalDate.parse(date, dateTimeFormatter); DayOfWeek dayOfWeek = dateFormat.getDayOfWeek(); return new DayOfTheWeek(String.valueOf(dayOfWeek)); } }위와 같이 DateTimeFormatter에서 패턴을 구해 옵니다. 그리고 LocalDate에서 파싱을 같은 형식으로 파싱을 해주죠.그리고 LocalDate에서 getDayOfWeek()로 요일을 구해 온 후 리턴해 주기만 하면 됩니다. ( getDayOfWeek() 만들어 주신 분 감사합니다 ) 문제 3번도 있습니다;ㅁ; 살려 주세요.{ "numbers": [1,2,3,4,5] }위와 같이 Post로 넘기면 합이 나와야 합니다.힌트가 있었는데요, 요청을 받는 DTO에서 List를 갖고 있으면 JSON의 배열로 받을 수 있다고 합니다.그런데 두둥. 안됩니다. 저는 안되었습니다.저는 아래와 같이 구현하였더니 잘되었습니다.@Getter @Setter @NoArgsConstructor public class NumbersRequest { private int[] numbers; public NumbersRequest(int[] numbers) { this.numbers = numbers; } }아래 컨트롤러에서는 스트림으로 처리해 보았습니다@RequestMapping("/api/v1") @RestController public class ArraySumController { @PostMapping("/sum") public int sum(@RequestBody NumbersRequest request){ // int sum = 0; // for (int number : request.getNumbers()) { // sum += number; // } return Arrays.stream(request.getNumbers()).sum(); } }[깃헙]과제가 너무 힘들었습니다.과제 하나에 3문제는 너무합니다 ㅠ그렇지만 다음 과제도 열심히 해보겠습니다!!!
백엔드
2024. 02. 18.
2
[1일차] 인프런 워밍업 스터디 클럽 0기- BE
[질문]어노테이션을 사용하는 이유 (효과) 는 무엇일까?나만의 어노테이션은 어떻게 만들 수 있을까?1일차에 위와 같은 질문을 받았습니다.어노테이션을 사용하는 이유는 어노테이션 하나로 마법과 같은 일이 일어나기 때문이라고 하셨습니다. ( 강의 중에.. )예를 들면 @PostMapping의 경우 이 어노테이션 하나로 http통신 방법을 정의할 수 있습니다.아래는 @PostMapping 어노테이션의 일부 코드입니다. ( 솔직히 어떻게 작동하는지 모르겠습니다 )@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.POST) public @interface PostMapping { /** * Alias for {@link RequestMapping#name}. */ @AliasFor(annotation = RequestMapping.class) String name() default ""; /** * Alias for {@link RequestMapping#value}. */ @AliasFor(annotation = RequestMapping.class) String[] value() default {}; /** * Alias for {@link RequestMapping#path}. */ @AliasFor(annotation = RequestMapping.class) String[] path() default {}; /** * Alias for {@link RequestMapping#params}. */ @AliasFor(annotation = RequestMapping.class) String[] params() default {}; 그럼 이제 나도 마법같은 나만의 어노테이션을 만들어 볼까? 라는 생각이 들었습니다.@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AnnotationCal { int[] numbers() default {}; }어떤 어노테이션을 만들어 볼까 하다가 배열을 넣으면 계산을 해주는 어노테이션을 만들기로 하였습니다.위와 같이 어노테이션을 선언 하였습니다. 간단하게 numbers 배열로 만들었습니다.public class AnnotationUtil { static int calculateSumForAnnotation(Class clazz, String methodName) throws NoSuchMethodException { int sum = 0; Method method = clazz.getMethod(methodName); AnnotationCal annotationCal = method.getAnnotation(AnnotationCal.class); for (int number : annotationCal.numbers()) { sum += number; } return sum; } }어노테이션 유틸에 덧셈 계산을 하는 로직을 만들어 주었습니다. 근데 @PostMapping의 경우는 저런 구현체가 없던데...@RestController public class AnnotationCalculator { @GetMapping("/annotationSum") @AnnotationCal(numbers = {1, 2, 3, 4, 5}) public int annotationSum() throws NoSuchMethodException { return AnnotationUtil.calculateSumForAnnotation(getClass(), "annotationSum"); } }위와 같이 어노테이션 배열로 숫자를 넣어 주면 자동으로 합계를 구해주도록 만들었습니다!!위 API를 호출하면 15가 출력됩니다![ 깃 허브 ]여기서 더 궁금한 점이 생겼습니다. 위에서 말한 구현체 관련된 부분입니다.스프링 어노테이션의 경우 위와같이 어노테이션을 불러서 로직을 만들어 주는 부분이 어디 있는지 모르겠습니다. 저처럼 어딘가에 만들어 놓지 않았다면 절대 동작할 일이 없습니다.저도 어노테이션만 선언하면 사용자는 세부 로직을 모르고 단순히 사용만 가능하도록 로직을 만들어 보고 싶습니다.그렇지만 저는 검색 바보라서 그런 것을 찾는 것이 너무 어렵습니다ㅠㅠ아시는 분은 좀 알려주세요~ 1일차 과제 끝 ~~
백엔드
2024. 02. 18.
2
[OT] 인프런 워밍업 스터디 클럽 0기- BE
인프런 워밍업 스터디 클럽에 참여하기로 결정하고 빠르게 신청했습니다. 지원 과정에서 인터뷰와 같은 자기 소개를 잘하는 것이 부족하여 탈락할 것이라고 생각했었죠. 그런데 의외로 합격 소식을 받게 되었습니다!현재는 프론트엔드에 큰 흥미를 느껴서 vue.js, typescript, nuxt.js 등을 공부 중이었는데, 갑자기 백엔드 공부의 기회가 생겨 기뻐하고 있습니다.좋은 소식에 기뻐하며 약속을 다 취소하고 오티에 참석했습니다. 그리고 개요를 듣고 해야 할 일을 천천히 살펴보니, 생각보다 과제와 분량이 상당히 많았습니다. 하루에 4~5시간을 투자해야 하는 양에 좀 놀랐죠. 저는 직장을 다니고 육아도 병행하고 있어서 하루에 이렇게 많은 시간을 공부에 할애하는 것이 조금 힘들었습니다.그래서 주말을 통해 1주차 과제를 모두 해결하고자 마음을 먹었습니다.다음에 계속...
백엔드