제텔
수강평 작성수
1
평균평점
5.0
블로그
전체 11#카테고리
- 백엔드
![[인프런 워밍업 클럽 0기] BE 7일차 과제](https://cdn.inflearn.com/public/files/blogs/e8921049-d277-46a7-a361-f9ac5fcee755/332957.png?w=260)
2024. 03. 13.
0
[인프런 워밍업 클럽 0기] BE 7일차 과제
문제 1과제 #6에서 만들었던 Fruit 기능들을 JPA를 이용하도록 변경해보세요!문제2우리는 특정 과일을 기준으로 지금까지 우리 가게를 거쳐갔던 과일 개수를 세고 싶습니다.에서 만들었던 과일 Entity Class를 이용해 기능을 만들어 보세요!예를 들어(1, 사과, 3000원, 판매 O)(2, 바나나, 4000원, 판매 X)(3, 사과, 3000원, 판매 O)와 같은 세 데이터가 있고, 사과를 기준으로 과일 개수를 센다면, 우리의 API는 2를 반환할 것입니다.구체적인 스펙은 다음과 같습니다.HTTP method : GETHTTP path : /api/v1/fruit/countHTTP queryname : 과일 이름예시 GET /api/v1/fruit/count?name=사과HTTP 응답 Body{ "count": long }HTTP 응답 Body 예시{ "count": 2 }문제 3우리는 아직 판매되지 않은 특정 금액 이상 혹은 특정 금액 이하의 과일 목록을 받아보고 싶습니다.구체적인 스펙은 다음과 같습니다.HTTP method : GETHTTP path : /api/v1/fruit/listHTTP queryoption : "GTE" 혹은 "LTE"라는 문자열이 들어온다.GTE : greater than equal의 의미LTE : less than equal의 의미price : 기준이 되는 금액이 들어온다.예시 1 - GET /api/v1/fruit/list?option=GTE&price=3000판매되지 않은 3000원 이상의 과일 목록을 반환해야 한다.예시 2 - GET /api/v1/fruit/list?option=LTE&price=5000판매되지 않은 5000원 이하의 과일 목록을 반환해야 한다.HTTP 응답 Body[{ "name": String, "price": long, "warehousingDate": LocalDate, }, ...]HTTP 응답 Body 예시[ { "name": "사과", "price": 4000, "warehousingDate": "2024-01-05", }, { "name": "바나나", "price": 6000, "warehousingDate": "2024-01-08", } ]후기워밍업 클럽이 마무리되고과제들을 제출하는 시점에하나가 비어있는 게 아쉬워서실패의 이유를 회고해보고자 한다.이전 발자국에서 이야기했던 것처럼(https://www.inflearn.com/blogs/6974)문제 1에서부터 막혔는데 public TotalPriceResponse getTotalPrice(String name) { String tSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = true"; String fSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = false"; long salesAmount = jdbcTemplate.queryForObject(tSql, long.class, name); long notSalesAmount = jdbcTemplate.queryForObject(fSql, long.class, name); return new TotalPriceResponse(salesAmount, notSalesAmount); }원인은 이 코드다.JDBC에서는 일단 돌아가게는 만들었으나JPA로 변경할 때Layered Architecture에서 각각의 역할을 분리해야되는데나머지 간단했던 코드에 비해 이색(?)적으로 구현했던 나의 코드는어디에 얼만큼 나누어 배치해야되는지 감이 잡히지 않았다.JDBC에서 일단 돌아가게는 만들어놓은 스파게티 코드를하나하나 역할에 따라 분리하려니이건 무슨 역할이고 어떻게 나눠야 되지?어디에 배치해야 하는 거지?Layered Architecture로 나누는 건 알겠는데Response와 request가 영향을 미치는 범위는 어느 정도로?같은 세부적인 코드레벨에서 기준을 잡기 어려워 명확한 코드가 안 나왔던 거 같다.(사실 윗 코드는 그냥 두 개 불러올 때 고려해야하는 여러 가지 이해에 필요한 공수를 감당하기 버거웠던 듯) 뭐 일단 돌아가게 만든다면야 아예 새로 구성해서 두 개의 호출을 합치는 방법이라던가(이건 처음 작성할 때의 목적에 어긋나는 거 같고..)특정 스프링의 어노테이션을 알면 구현은 가능할 듯 했으나그 정도를 이해하고 다루려면 너무 거대한 키워드들이라당초의 목적인 백엔드 전반 체험에서는 너무 멀어지는 길이기도 하고뭣도 모르는 초보자가 갈 길과 공수는 아닌 듯 했다.(갖다 써서 해결은 학습의 목적은 아니니... 그렇다고 스프링을 다 알 수도 없잖아...) 강의를 듣고 JDBC Hibernate JPA등의 여러 추상화 단계를 거쳐 객체로써 다룰 수 있다는 건 알겠는데세부적인 사항까지 파악하여 과제를 하기엔프론트 개발을 하던 나에겐 너무 멀리 가는 방향이어서 멈칫하게 되기도 했다. 그리하여 개선을 하는 문제 1부터 막혀진행해보다가 실패하게 되었고무엇을 해야하는지는 보였으나"일단 동작"이 아닌 학습의 목적인 "동작 원리 이해"로 접근하기엔 능력 미달이었다. 그래서 뭐 결론적으론 과제는 미해결 상태로 남게 되었다. Nextjs에도 쿼리 파라미터가 있는데비즈니스 도메인에 따라 어디서 어떻게 처리하는 게 좋은지가 갈리는 건가여러 생각해볼 거리도 생긴 듯 하다.(이건 인프라인가 백엔드인가 프론트인가)
백엔드
![[인프런 워밍업 클럽 0기] BE 마지막 발자국 👣](https://cdn.inflearn.com/public/files/blogs/ba2e4f09-cb86-4a0f-9b6d-3d6212fd6f15/332957.png?w=260)
2024. 03. 10.
0
[인프런 워밍업 클럽 0기] BE 마지막 발자국 👣
일주일 간의 학습 내용JPA 더 다뤄보기스프링 부트 톺아보기Git과 배포 후기약 3주간 워밍업 클럽을 진행하면서WAS가 API를 보내는 방식에 대해 알 수 있게 되었고스프링의 어노테이션은 이해보단 일단 정보성으로이걸 쓰면 이렇게 해준다더라는 느낌으로 맛보고JPA로 SQL을 직접 다루진 않는 법도 있다는 여러 단계의 추상화를 경험했다. 처음 시작할 땐 호기롭게 미션 올클리어까지 해보자는 마음이었지만자바와 친숙하지 못했던터라여러 지점에서이게 왜 되지?이건 왜 안 되지?를 마주한 뒤할 수 있는만큼이라도원래 계획했던 최소치라도 필수로마무리하자라는 쪽으로 방향을 틀게 되었다... ㅠ 당분간은 JS 진영으로 돌아가 NextJS와 함수형 프로그래밍을 공부할 예정이지만이번 워밍업 클럽에서 배운 것을 바탕으로 그 이후에 Nest나 코프링도 한 번 만져봐야겠다!그 전에 OOP나 이해하고 와라
백엔드
![[인프런 워밍업 클럽 0기] BE 미니 프로젝트 1단계](https://cdn.inflearn.com/public/files/blogs/0273b92c-9ec9-4c11-bf42-3c6a4e99443a/332957.png?w=260)
2024. 03. 07.
0
[인프런 워밍업 클럽 0기] BE 미니 프로젝트 1단계
Team 기능Controller@RestController public class TeamController { private final TeamService teamService; public TeamController(TeamService teamService) { this.teamService = teamService; } @PostMapping("/team") public void saveTeam(@RequestBody TeamCreateRequest request) { teamService.saveTeam(request); } @GetMapping("/team") public List getTeam() { return teamService.getTeams(); } }Domain@Entity public class Team { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(nullable = false, length = 25) private String name; private String managerName; private long memberCount; public Team(String name) { this.name = name; } public void setTeamManager(String name){ this.managerName = name; } public void setMemberCount(){ this.memberCount++; } public String getName() { return name; } public String getManagerName() { return managerName; } public long getMemberCount() { return memberCount; } public long getId() { return id; } }Service@Service public class TeamService { private final TeamRepository teamRepository; public TeamService(TeamRepository teamRepository) { this.teamRepository = teamRepository; } @Transactional public void saveTeam(TeamCreateRequest request){ teamRepository.save(new Team(request.getName())); } public List getTeams(){ List teams = teamRepository.findAll(); return teams.stream() .map(TeamFindResponse::new) .collect(Collectors.toList()); } }Member 기능Controller@RestController public class MemberController { private final MemberService memberService; public MemberController(MemberService memberService) { this.memberService = memberService; } @PostMapping("/member") public void saveMember(@RequestBody MemberCreateRequest request) { memberService.saveMember(request); } @GetMapping("/member") public List getMember() { return memberService.getMember(); } }Domain@Entity public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, length = 25) private String name; private Long teamId; @Enumerated(EnumType.STRING) private Role role; private LocalDate birthday; private LocalDate workStartDate; public Member(Long id, String name, Long teamId, Role role, LocalDate birthday, LocalDate workStartDate) { this.id = id; this.name = name; this.teamId = teamId; this.role = role; this.birthday = birthday; this.workStartDate = workStartDate; } //getters }public enum Role { Manager, Member; }Service@Service public class MemberService { private final MemberRepository memberRepository; public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } @Transactional public void saveMember(MemberCreateRequest request){ Member member = memberRepository.findByName(request.getTeamName()) .orElseThrow(IllegalArgumentException::new); Role role = request.isManager() ? Role.Manager : Role.Member; memberRepository.save( new Member( request.getName(),request.getTeamId(), role, request.getBirthday(), request.getWorkStartDate() ) ); if(role.equals(Role.Manager)){ team.setTeamManager(request.getName()); memberRepository.save(team); } team.setMemberCount(); memberRepository.save(team); } public void getMember() { List response = new ArrayList(); List members = memberRepository.findAll(); for (Member member : members) { response.add( new MemberFindResponse( member.getName(), member.getTeamId(), member.getRole(), member.getBirthday(), member.getWorkStartDate() ) ); } return response; } } 후기아무래도 기초가 없이 진행하려다 보니 여러가지가 맞물려서 오류가 일어날 때어떻게 개선해야할지 길을 잃는 경우가 많은 것 같다.기능별로 구분하기 위해 Team과 Member를 나누어 각각 기능 구현은 가능하지만테이블을 참조할 경우 어떻게 객체 사이를 오가게 구현해야하지?같은 조금의 응용할 거리가 나와도 구현이 불가능해진다...테이블을 중복해서 생성하는 건 당연히 맞는 방향이 아닌데그럼 member에서 team을 불러오는 게 맞는 건가? 하려면 할 순 있는데 아닌 것 같은데?같은... 데서 구현에 병목들이 생기다보니 확장불가능한 구현만 가능하고2~4단계로는 나아가지 못했다 ㅠㅠ(다른 분들의 깃헙을 참고해도 잘 적용이 안 되는 무지한 나...)
백엔드
![[인프런 워밍업 클럽 0기] BE 2주차 발자국 👣](https://cdn.inflearn.com/public/files/blogs/884dc158-3182-441b-84ca-f42638936440/332957.png?w=260)
2024. 03. 03.
0
[인프런 워밍업 클럽 0기] BE 2주차 발자국 👣
일주일 간의 학습 내용JPA와 트랜잭션CRUD 고도화와 보다 객체지향적!개발 서버와 배포 서버 분리, AWS 세팅2주차 미션6일차: 스프링 컨테이너의 의미와 사용법https://www.inflearn.com/blogs/6858 후기지난 주와 달리 매일 해왔던 미션이 사라지고 전반적인 수강 진도를 나가게 되었다.6일차, 7일차로 미션이 진행되면서 스프링의 전체를 이용하고JDBC기반 SQL을 JPA로 구현하게 되는데그동안 나무만 보면서 굴려왔던 게숲을 보면서 구현하려니 이해가 안되는 지점들을 많이 마주하게 되었다. Controller - Service - Repository로의 분리는완성도가 미진하던 말던일단 이해하는 대로 나누는 것은 가능했으나SQL이 아닌 JPA로 구현하려니 마주한 에러들은어디에 뭐가 없어서 작동을 안하는지 파악하기 어려운 것들이었다. JS를 써왔다보니 정적 타이핑으로 여기저기를 오가는 데이터의 흐름도 파악하기 어렵고 낯선데어노테이션으로 마법같이 처리를 해준다거나 application.yml 세팅을 추가해야 해결된다거나같은 것들을 표면적이 아니라 근본적으로 알아야 해결이 가능해보였다.하나씩은 검색해서 대충 알겠으나 여러개가 섞이니 뭐가 문젠지도 알 수 없는...결과적으로 7일차 과제는 여러번 시도해보았지만 API를 모두 바꾸는데는 실패했고수정할수록 새로운 에러를 마주하는 기묘한 상황에넘쳐나는 키워드들로 압도당했다...! 결국은 당초의 목적으로 돌아가 완성도는 떨어지겠으나 웹 전반의 흐름을 인지하도록 방향 설정을 한뒤JPA는 추후 필요할 때 마주하고, 스프링은 필요하다면? 더 다루겠지란 마음으로 마무리하고강의 진도를 마저 나가게 되었다. 중간 특강과 강의를 통해 WAS와 웹서버, DB에서 어떤 식으로 일이 일어나겠구나란 감이 생겼고어떤 역할을 맡고 분리가 되는지에 대한 경계를 인식할 수 있게 된 것 같다. 미니 프로젝트도 시간이 별로 없지만 할 수 있는 만큼 해보자! (JPA 써서 될까? 객체 여러개 안 합치면 상관 없겠지...)
백엔드
![[인프런 워밍업 클럽 0기] BE 6일차 과제](https://cdn.inflearn.com/public/files/blogs/bd4cdfb7-b574-41ef-ab2e-6f9255f9c18a/332957.png?w=260)
2024. 02. 26.
0
[인프런 워밍업 클럽 0기] BE 6일차 과제
문제 1과제 #4에서 만들었던 API를 강의 내용처럼 Controller - Service - Repository로 분리해보세요!Controller@RestController public class FruitController { private final FruitService fruitService; public FruitController(FruitService fruitService) { this.fruitService = fruitService; } @PostMapping("/api/v1/fruit") // POST /api/v1/fruit public void saveFruit(@RequestBody FruitCreateRequest request) { fruitService.saveFruit(request); } @PutMapping("/api/v1/fruit") public void updateFruit(@RequestBody FruitUpdateRequest request) { fruitService.updateFruit(request); } @GetMapping("/api/v1/fruit/stat") // GET /api/v1/fruit/stat public TotalPriceResponse getTotalPrice(@RequestBody TotalPriceRequest request) { fruitService.getTotalPrice(request); } }Servicepublic class FruitService { private final FruitRepository fruitRepository; public FruitService(JdbcTemplate jdbcTemplate) { fruitRepository = new FruitRepository(jdbcTemplate); } public void updateFruit(FruitUpdateRequest request) { if (fruitRepository.isFruitNotExist(request.getId())) { throw new IllegalArgumentException(); } fruitRepository.updateFruitId(request.getId()); } public void saveFruit(FruitCreateRequest request) { fruitRepository.saveFruit(request.getName(),request.getWarehousingDate(),request.getPrice()); } public TotalPriceResponse getTotalPrice(TotalPriceRequest request) { fruitRepository.getTotalPrice(request.getName()); } }Repositorypublic class FruitRepository { private final JdbcTemplate jdbcTemplate; public FruitRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public boolean isFruitNotExist(long id) { String readSql = "SELECT id from fruit WHERE id = ?"; return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty(); } public void updateFruitId(long id) { String sql = "UPDATE fruit SET is_sold = TRUE WHERE id = ?"; jdbcTemplate.update(sql, id); } public void saveFruit(String name, LocalDate warehousingDate, long price) { String sql = "INSERT INTO fruit (name, warehousingDate, price) VALUES (?, ?, ?)"; jdbcTemplate.update(sql, name, warehousingDate, price); } public TotalPriceResponse getTotalPrice(String name) { String tSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = true"; String fSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = false"; long salesAmount = jdbcTemplate.queryForObject(tSql, long.class, name); long notSalesAmount = jdbcTemplate.queryForObject(fSql, long.class, name); return new TotalPriceResponse(salesAmount, notSalesAmount); } }실행 결과나머지 API들은 모두 정상 동작하지만, getTotalPrice는 repository에서 controller로 값이 전달되지 않는 현상이 발생했다.두 SQL문을 받아 객체를 생성하여 반환하던 방식이함수를 통해 jdbc를 전달하던 걸 생성자로 바꿔서 문제가 생긴건지스프링과 어노테이션의 작동방식에서 인식을 못하는 건지반환 방식을 지정해주면서 void가 아닌 경우 처리할 것들이 더 필요한 건지아니면 여러 개가 섞여서 그런건지아는 바가 없어 분리에는 성공했지만, 실행에는 1 실패를 기록하였다...문제 2문제 1에서 코드가 분리되면 FruitController/ FruitService / FruitRepository 가 생겼을 것입니 다.기존에 작성했던 FruitRepository를 FruitMemoryRepository 와 FruitMySqlRepository로 나누고 @Primary 어노테이션을 활용해 두 Repository를 바꿔가며 동작시킬 수 있도록 코드를 변경해보세요!FruitRepositorypublic interface FruitRepository { boolean isFruitNotExist(long id); void updateFruitId(long id); void saveFruit(String name, LocalDate warehousingDate, long price); TotalPriceResponse getTotalPrice(String name); }FruitMySqlRepository(FruitMemoryRepository)@Primary // 쓸 곳에만 작성! @Repository public class FruitMySqlRepository implements FruitRepository { ... }
백엔드
![[인프런 워밍업 클럽 0기] BE 1주차 발자국 👣](https://cdn.inflearn.com/public/files/blogs/24d68c82-f3d8-414c-bae5-7cd33dd29acb/332957.png?w=260)
2024. 02. 25.
0
[인프런 워밍업 클럽 0기] BE 1주차 발자국 👣
일주일 간의 학습 내용어노테이션을 활용한 간단한 CRUD API 만들기RDB(MySQL)과 연동스프링 디자인 패턴?(OCP?) 꾸준한 학습! 1주차 미션1일차: 서버 개발을 위한 환경 설정 및 네트워크 기초https://www.inflearn.com/blogs/65642일차: 첫 HTTP API 개발https://www.inflearn.com/blogs/65983일차: 기본적인 데이터베이스 사용법https://www.inflearn.com/blogs/66554일차: 데이터베이스를 사용해 만드는 APIhttps://www.inflearn.com/blogs/66905일차: 클린코드의 개념과 첫 리팩토링https://www.inflearn.com/blogs/6718 후기문제를 해결하는 과정이나 학습 내용에 대한 회고를 적자니그저 검색, 동영상 재시청 등이나 단순한 정보의 나열 정도일 뿐유의미한 수준의 글 내용이 없을 것 같아1주일간 워밍업 클럽의 후기?를 작성하고자 한다.일단 자바를 배워본 적도 없고 프론트엔드로 Nextjs를 써본 경험 정도만이 있어OOP에 대해도, 기타 백엔드에 대해서도 이론적인 부분만 일부 알고 있을 뿐실제 개발에서 어떻게 쓰고 어떻게 적용되는지는 상상의 영역이었다.(다형성? 붕어빵틀? 그게 그래서 뭔데;;)이런 백엔드의 전반적인 부분을 초심자의 입장에서 다룬다고 했을 때각각의 파트도 사실 방대하고 무엇을 어느 정도 해야하는지 판단하기도 애매한데필요한 것들을 최대한 경량화해서 잘 전달해주신다는 느낌을 받았다.'API로 JSON 객체를 전달해준다' 정도의 블랙박스였던 백엔드가 확실히 선명해져 가니마지막즈음엔 배포를 해서 웹 전반의 흐름을 인지하고 트레이드오프를 이해하는 개발자가 되어보자!
백엔드
![[인프런 워밍업 클럽 0기] BE 5일차 과제](https://cdn.inflearn.com/public/files/blogs/5921ea12-e53a-44c2-a294-e0c63bf5a9c0/332957.png?w=260)
2024. 02. 23.
0
[인프런 워밍업 클럽 0기] BE 5일차 과제
클린 코드최대한 클린하지 않게 작성된 아래 코드는 다음과 같이 동작합니다.주어지는 숫자를 하나 받는다.해당 숫자만큼 주사위를 던져, 각 숫자가 몇 번 나왔는지 알려준다.public class Main { public static void main(String[] args) throws Exception { System.out.print("숫자를 입력하세요 : "); 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 static void main(String[] args) { // 사용자로부터 숫자 입력 받기 System.out.print("숫자를 입력하세요 : "); Scanner scanner = new Scanner(System.in); int numberOfThrows = scanner.nextInt(); // 주사위 던지기 결과를 저장할 배열 int[] diceResults = new int[6]; // 주사위를 numberOfThrows만큼 던지고 결과를 기록 for (int i = 0; i 한 걸음 더!현재 코드는 주사위가 1~6까지만 있다는 가정으로 작성되어 있습니다.따라서 주사위가 1~12까지 있거나 1~20까지 있다면 코드를 많이 수정해야 하죠! 위의 코드를 클린하게 개선해 보았다면, 주사위의 숫자 범위가 달라지더라도 코드를 적게 수정할 수 있도 록 고민해 봅시다!public static void main(String[] args) { // 사용자로부터 숫자 입력 받기 System.out.print("주사위를 던질 숫자를 입력하세요 : "); System.out.print("주사위의 최댓값을 입력하세요 : "); Scanner scanner = new Scanner(System.in); int numberOfThrows = scanner.nextInt(); int diceMaxNumber = scanner.nextInt(); // 주사위 던지기 결과를 저장할 배열 int[] diceResults = new int[6]; // 주사위를 numberOfThrows만큼 던지고 결과를 기록 for (int i = 0; i
백엔드
![[인프런 워밍업 클럽 0기] BE 4일차 과제](https://cdn.inflearn.com/public/files/blogs/e0deb873-f57d-4e89-af5e-1523b7a9c2e7/332957.png?w=260)
2024. 02. 22.
0
[인프런 워밍업 클럽 0기] BE 4일차 과제
문제 1우리는 작은 과일 과게를 운영하고 있습니다. 과일 가게에 입고된 "과일 정보"를 저장하는 API를 만드는 스펙은 다음과 같습니다.HTTP method: POSTHTTP path: /api/v1/fruitHTTP 요청 Body{ "name": String, "warehousingDate": LocalDate, "price": long }HTTP요청 Body 예시{ "name": "사과", "warehousingDate": "2024-02-01", "price": 5000 }응답: 성공시 200Controller@RestController public class FruitController { private final JdbcTemplate jdbcTemplate; public FruitController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @PostMapping("/api/v1/fruit") // POST /api/v1/fruit public void saveFruit(@RequestBody FruitCreateRequest request) { String sql = "INSERT INTO fruit (name, warehousingDate, price) VALUES (?, ?, ?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); } }Requestpublic class FruitCreateRequest { private String name; private LocalDate warehousingDate; private long price; public String getName() { return name; } public LocalDate getWarehousingDate() { return warehousingDate; } public long getPrice() { return price; } }시행착오DB를 library 밖에 만들면 기존 프로젝트에서 연동이 안 되어MySQL 콘솔에 use fruit를 쓰지 않고 use library를 한 뒤Fruit 테이블을 databases에 만들었다가 library 내부에 다시 만들어 참조함 실행 결과한 걸음 더!위 API에서 long을 사용한 이유 가격의 최댓값을 고려하여 DB에서 가장 큰 bigint를 사용한다. 따라서 자바에서 long 작성하게 된다.문제 2과일이 팔리게 되면, 우리 시스템에 팔린 과일 정보를 기록해야 합니다. 스펙은 다음과 같습니다.HTTP method: PUTHTTP path: /api/v1/fruitHTTP 요청 Body{ "id": long }HTTP 요청 Body 예시{ "id": 3 }응답 : 성공시 200문제 3과 같이 보았을 때, 요구사항은 id를 기준으로 팔린 과일을 반영하고자 하는 것으로 보입니다.우선, id를 요청하면 해당 과일이 존재할 경우 그 과일의 is_sold를 기본값 False에서 True로 바꿔봅시다.(사실 요구사항이 정확히 맞는지는 모르겠음) 연습이니까 Update Code Kata 라고 생각하고 걍 해보자Controller @PutMapping("/api/v1/fruit") public void updateFruit(@RequestBody FruitUpdateRequest request) { String readSql = "SELECT id from fruit WHERE id = ?"; boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty(); if (isUserNotExist) { throw new IllegalArgumentException(); } String sql = "UPDATE fruit SET is_sold = TRUE WHERE id = ?"; jdbcTemplate.update(sql,request.getId()); }실행 결과변경 전변경 후존재하지 않는 ID 호출 시500 에러 확인문제 3우리는 특정 과일을 기준으로 팔린 금액, 팔리지 않은 금액을 조회하고 싶습니다.예를 들어(1, 사과, 3000원, 판매 O)(2, 사과, 4000원, 판매 X)(3, 사과, 3000원, 판매 O)와 같은 세 데이터가 있다면 우리의 API는 판매된 금액: 6000원, 판매되지 않은 금액 : 4000원 이라고 응답해야 합니다.HTTP method: GETHTTP path:/api/v1/fruit/statHTTP queryname: 과일 이름예시 GET /api/v1/fruit/stat?name=사과HTTP 응답 Body{ "salesAmount": long, "notSalesAmount": long }HTTP 응답 Body 예시{ "salesAmount": 6000, "notSalesAmount": 4000 }TotalPriceResponsepublic class TotalPriceResponse { private final long salesAmount; private final long notSalesAmount; public TotalPriceResponse(long salesAmount, long notSalesAmount) { this.salesAmount = salesAmount; this.notSalesAmount = notSalesAmount; } public long getSalesAmount() { return salesAmount; } public long getNotSalesAmount() { return notSalesAmount; } }Controller @GetMapping("/api/v1/fruit/stat") // GET /api/v1/fruit/stat public TotalPriceResponse getTotalPrice(@RequestParam String name) { String tSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = true"; String fSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = false"; long salesAmount = jdbcTemplate.queryForObject(tSql, long.class, name); long notSalesAmount = jdbcTemplate.queryForObject(fSql, long.class, name); return new TotalPriceResponse(salesAmount, notSalesAmount); }실행 결과
백엔드
![[인프런 워밍업 클럽 0기] BE 3일차 과제](https://cdn.inflearn.com/public/files/blogs/67be9549-d3d7-495f-a465-c76e00a575c3/332957.png?w=260)
2024. 02. 21.
0
[인프런 워밍업 클럽 0기] BE 3일차 과제
자바의 람다식은 왜 등장했을까?이전에는 자바에서 함수형 프로그래밍을 구현하려면 인터페이스를 정의하고, 그 인터페이스를 구현한 클래스를 만들어야 했습니다.(비록 익명 클래스와 인터페이스로 무수한 메소드 생성을 막을 수는 있으나...) 이는 코드의 양을 불필요하게 늘리고, 가독성을 해치는 요소였습니다. 람다식은 이러한 불편함을 해소하고, 간결하고 가독성 있는 코드를 작성할 수 있도록 도와줍니다. 람다식과 익명 클래스는 어떤 관계가 있을까?람다식과 익명 클래스는 두 가지가 비슷한 측면이 있습니다. 둘 다 인터페이스를 구현하는 객체를 생성하는데 사용될 수 있습니다. 익명 클래스는 인터페이스를 구현하는 익명의 클래스를 만들어 사용하는 반면, 람다식은 함수형 인터페이스를 구현하는 익명 메서드의 구현체로서 사용됩니다. 따라서 람다식은 익명 클래스의 간단한 표현이라고 볼 수 있습니다. 람다식의 문법은 어떻게 될까?변수 -> 변수를 이용한 함수(변수1, 변수2, ...) -> 변수1과 변수2, (...)를 이용한 함수 @FunctionalInterface / 스트림 API / 메소드 레퍼런스 추가적으로 람다식은 함수형 프로그래밍의 핵심 요소 중 하나로, 자바 8에서 도입되었습니다. 이것은 함수형 인터페이스(Functional Interface)를 간결하게 구현하는 방법을 제공합니다. 함수형 인터페이스는 단 하나의 추상 메소드를 가지고 있으며, 이는 주로 람다식을 사용하여 구현됩니다. 이러한 함수형 인터페이스에는 @FunctionalInterface 어노테이션을 사용하여 명시적으로 표시할 수 있습니다. 람다식은 메소드 레퍼런스를 사용해 "메소드 자체를 직접 넘겨주는 것처럼" 사용해 대체될 수 있고, 간결한 스트림 api는 반복문을 단축시키며 병렬처리를 진행할 때 큰 강점이 있습니다.
백엔드
![[인프런 워밍업 클럽 0기] BE 2일차 과제](https://cdn.inflearn.com/public/files/blogs/2c7ac7cd-bb14-4fb6-b663-5e2ddb298d84/332957.png?w=260)
2024. 02. 20.
0
[인프런 워밍업 클럽 0기] BE 2일차 과제
문제 1두 수를 입력하면, 다음과 같은 결과가 나오는 GET API를 만들어 보자!path : /api/v1/calc이다.쿼리 파라미터 : num1, num2{ "add": 덧셈결과, "minus": 뺄셈결과, "multiply": 곱셈결과 }dto/calculator/request/CalculatorRequest.javapublic class CalculatorRequest { private final int num1; private final int num2; public CalculatorRequest(int num1, int num2) { this.num1 = num1; this.num2 = num2; } public int getNum1() { return num1; } public int getNum2() { return num2; } }dto/calculator/response/CalculatorResponse.javapublic class CalculatorResponse { private final int add; private final int minus; private final int multiply; public CalculatorResponse(int add, int minus, int multiply) { this.add = add; this.minus = minus; this.multiply = multiply; } public int getAdd() { return add; } public int getMultiply() { return multiply; } public int getMinus() { return minus; } }controller/calculator/CalculatorController.java@RestController public class CalculatorController { @GetMapping("/api/v1/calc") // GET /api/v1/calc public CalculatorResponse calculateTwoNumbers(CalculatorRequest request) { int add = request.getNum1() + request.getNum2(); int minus = request.getNum1() - request.getNum2(); int multiply = request.getNum1() * request.getNum2(); return new CalculatorResponse(add, minus, multiply); } }실행결과문제 2날짜를 입력하면, 몇 요일인지 알려주는 GET API를 만들어 보자!path와 쿼리 파라미터는 임의로 만들어도 상관없다.dto/study/DayResponse.javapublic class DayResponse { private final String dayOfWeek; public DayResponse(String dayOfWeek) { this.dayOfWeek = dayOfWeek; } public String getDayOfWeek() { return dayOfWeek; } }controller/study/DayController.java@RestController public class DayController { @GetMapping("/day") // GET /day public DayResponse dayOfWeek(@RequestParam String date) { LocalDate localDate = LocalDate.parse(date); String dayOfWeek = localDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.US); return new DayResponse(dayOfWeek); } }실행결과 문제 3여러 수를 받아 총 합을 반환하는 POST API를 만들어 보자!API에서 받는 Body는 다음과 같은 형태이다. (HINT : 요청을 받는 DTO에서 List를 갖고 있으면 JSON의 배열을 받을 수 있습니다){ "numbers": [1, 2, 3, 4, 5] }반환 결과15dto/calculator/request/MultiAddRequest.javapublic class MultiAddRequest { private List numbers; public MultiAddRequest(List numbers) { this.numbers = numbers; } public MultiAddRequest() { } public List getNumbers() { return numbers; } }dto/calculator/response/MultiAddResponse.javapublic class MultiAddResponse { private final Integer sum; public MultiAddResponse(Integer sum) { this.sum = sum; } public Integer getSum() { return sum; } }controller/calculator/CalculatorController.java@RestController public class MultiAddController { @PostMapping("/multi") public MultiAddResponse MultiAdd(@RequestBody MultiAddRequest request) { int sum = request.getNumbers().stream() .mapToInt(i -> i) .sum(); return new MultiAddResponse(sum); } } 실행 결과
백엔드




