문정환
수강평 작성수
-
평균평점
-
블로그
전체 10![[인프런 워밍업 클럽 0기] BE 3주차 발자국](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 03. 10.
0
[인프런 워밍업 클럽 0기] BE 3주차 발자국
일주일 간의 학습내용객체지향JPA 연관관계미니 프로젝트3주차를 진행하면서, 2주차에 이어 JPA에 대해 추가적인 학습을 하게되었다. 객체지향과 데이터베이스의 형태는 다르기 때문에 JPA의 연관관계 @OneToMany, @ManyToOne의 키워드에 대해 학습하였다. 또한 3주차를 진행하면서 미니프로젝트 STep1까지의 프로젝트를 만들어보았으며 추후 Step 2, 3, 4를 해볼 예정이다. 금요일 라이브 회고금요일 라이브에서는 코치님의 코드리뷰 라이브를 보았다.모두에게 공개되는 자신의 코드에 부끄럼없이 보여주신분들에게 감사를 표한다. 이번 코드리뷰에서는 더욱 객체지향적인 코드를 만드는 방법은 물론이며, 코치님이 생각하는 클린코드에 대해 듣게되었다. 우선 가장 기억에 남는부분은 Service 계층이 뚱뚱해지는 것을 막으려면, 도메인이 처리할 수 있는 부분은 도메인에 구현하라였다. 그렇게 도메인에 책임을 부여하게되면 Service 계층이 가벼워지고, 더욱 읽기 쉬운 코드가 되었다는 부분이었다. 두번째로는, Enum을 이용하여 Role 부분의 확장을 생각해보는 부분이었다. Enum을 자주쓰지 않았기 때문에, 어떤식으로 써야할지 감이 잘 오지 않았지만, 이번에 리뷰를 들으며 나의 프로젝트에서도 Enum을 사용하여 리팩토링을 해볼 예정이다. 마지막으로는, 헥사고날 아키텍쳐에 대해 간단하게 들었던 부분이다. 현재 널리사용되고 있는 Layered Architecture는 데이터베이스에 너무 의존적이기 때문에, 현재 코드 그대로 데이터베이스만 바꿀수 없지만, 헥사고날 아키텍쳐는 데이터베이스만 바꾸는 것이 가능하다. 얘기만 들었을때는, 추상화에 더 신경쓴 아키텍쳐라 생각되어 코드를 작성할 때에는 시간적인 비용이 많이들겠지만, 설계를 다 마치고나면 유지보수와 코드를 읽는데에는 훨씬 좋을 것 이라고 생각된다. 이번 워밍업 클럽을 통해 발전한 자신을 볼 수 있었으며, 좋은 기회를 주신 인프런과 최태현 코치님께 감사의 말씀을 올리며 마무리하겠습니다. 감사합니다
백엔드
![[인프런 워밍업 클럽 0기] BE 2주차 발자국](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 03. 03.
0
[인프런 워밍업 클럽 0기] BE 2주차 발자국
일주일 간의 학습내용스프링 컨테이너JPA트랜잭션6일차: 스프링 컨테이너의 의미와 사용 방법스프링 컨테이너가 하는 역할이 무엇인지, 그로인해 얻을 수 있는 장점이 무엇인지 학습하였다.7일차: Spring Data JPA를 사용한 데이터베이스 조작Spring이 제공하는 Spring Data JPA를 이용하여 편리하게 CRUD를 구성해보았다8일차: 트랜잭션과 영속성 컨텍스트@Transactional 어노테이션의 학습과 JPA의 영속성 컨텍스트를 이용해 캐시의 개념처럼 사용한다는 것을 알게되었다.9일차: 조금 더 복잡한 기능을 API로 구성하기JPA를 이용해 API로 만들어 보았다. 2주차 미션6일차: Controller, Service, Repository 계층구조에 대해 익숙해지기https://www.inflearn.com/blogs/68557일차: 과제 6에서 만들었던 Fruit을 JPA로 리팩토링하기https://www.inflearn.com/blogs/6895 회고2주차를 진행하면서 Spring의 기본적인 원리와 JPA의 간단한 사용방법을 알아보았다. 다음주엔 미니프로젝트를 진행해 볼 차례인데 지금까지 배웠던걸 잘 녹여내서 미니프로젝트를 만들어볼 것 이다.
![[인프런 워밍업 클럽] BE 0기 7일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 27.
0
[인프런 워밍업 클럽] BE 0기 7일차 과제
Fruit)import javax.persistence.*; import java.time.LocalDate; @Entity public class Fruit { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(length = 30) private String name; private LocalDate warehousingDate; private long price; private boolean isSold; protected Fruit(){} public Fruit(String name, LocalDate warehousingDate, long price) { this.name = name; this.warehousingDate = warehousingDate; this.price = price; this.isSold = false; } public Long getId() { return id; } public String getName() { return name; } public LocalDate getWarehousingDate() { return warehousingDate; } public long getPrice() { return price; } public boolean isSold() { return isSold; } public void setSold(boolean sold) { isSold = sold; } }primary key인 id 설정을 해주었으며, Entity로 사용하기 위해 빈 생성자를 만들어 주었다. Fruit 테이블 상태)요청을 위한 FruitController 메소드) @GetMapping("/api/v1/fruit/count") public CountResponse countFruit(@RequestParam String name) { return fruitService.countFruit(name); }FruitService 메소드)@Service public class FruitService { private final FruitRepository fruitRepository; private final FruitJpaRepository jpaRepository; public FruitService(FruitRepository fruitRepository, FruitJpaRepository jpaRepository) { this.fruitRepository = fruitRepository; this.jpaRepository = jpaRepository; } public CountResponse countFruit(String name) { List fruits = jpaRepository.findAllByName(name); return new CountResponse(fruits.size()); } } 기존 FruitService에서 변경점이 생겼다. JpaRepository사용을 위해 FruitJpaRepository 인터페이스를 생성자로 받아줬으며, 인터페이스의 메소드를 정의 후 사용하였다.결과)정상적으로 나오는걸 확인할 수 있었다.문제를 확인했을 때, 스프링 데이터 JPA의 쿼리문을 이용하여 메소드명을 작성해야 할 것으로 생각하였다. 또한 응답 body의 형태는 List의 형태를 취했으며 List를 사용하였다.컨트롤러) @GetMapping("/api/v1/fruit/list") public List notSoldFruitList(FruitOptionRequest request) { return fruitService.notSoldFruitList(request); }서비스) public List notSoldFruitList(FruitOptionRequest request) { return getFruits(request).stream() .map(FruitResponse::new) .collect(Collectors.toList()); } private List getFruits(FruitOptionRequest request) { if (request.getOption().equals("GTE")) { System.out.println("hi"); return jpaRepository.findAllByPriceGreaterThanEqualAndIsSoldFalse(request.getPrice()); } else { return jpaRepository.findAllByPriceLessThanEqualAndIsSoldFalse(request.getPrice()); } }request option으로 넘어온 데이터를 확인하기 위한 메소드를 만들어 작성하였다. 그 결과로 주 목적인 notSoldFruitList의 코드는 간결해졌으며, 서비스 계층에서 중요한 역할인 비즈니스를 만을 위한 코드를 작성 해주었다. FruitJpaRepository)import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface FruitJpaRepository extends JpaRepository { List findAllByName(String name); List findAllByPriceGreaterThanEqualAndIsSoldFalse(Long price); List findAllByPriceLessThanEqualAndIsSoldFalse(Long price); }이상을 위한 GreatherThanEqual, 이하를 위한 LessThanEqual을 사용해주었으며, IsSold의 boolean 값 체크를 위해 False를 이어주었다.결과)GTE, LTE 모두 정상적인 값을 출력하였다.
![[인프런 워밍업 클럽] BE 0기 6일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 26.
0
[인프런 워밍업 클럽] BE 0기 6일차 과제
컨트롤러)@RestController public class FruitController { private final FruitService fruitService; public FruitController(FruitService fruitService) { this.fruitService = fruitService; } @PostMapping("/api/v1/fruit") public void saveFruit(@RequestBody FruitRequest request) { fruitService.saveFruit(request); } @PutMapping("/api/v1/fruit") public void saleFruit(@RequestBody SoldOutFruitRequest request) { fruitService.saleFruit(request); } @GetMapping("/api/v1/fruit/stat") public TotalPriceResponse salesData(@RequestParam String name) { return fruitService.salesData(name); } }서비스)@Service public class FruitService { private final FruitRepository fruitRepository; public FruitService(FruitRepository fruitRepository) { this.fruitRepository = fruitRepository; } public void saveFruit(FruitRequest request) { fruitRepository.saveFruit(request); } public void saleFruit(SoldOutFruitRequest request) { boolean isEmpty = fruitRepository.checkSoldOut(request); if (isEmpty) { throw new IllegalArgumentException(); } fruitRepository.saleFruit(request); } public TotalPriceResponse salesData(String name) { return new TotalPriceResponse(fruitRepository.salesRead(name), fruitRepository.notSalesRead(name)); } } 레포지토리 인터페이스)public interface FruitRepository { void saveFruit(FruitRequest request); void saleFruit(SoldOutFruitRequest request); boolean checkSoldOut(SoldOutFruitRequest request); long salesRead(String name); long notSalesRead(String name); } FruitMemoryRepository)@Primary @Repository public class FruitMemoryRepository implements FruitRepository { private final List fruits = new ArrayList(); @Override public void saveFruit(FruitRequest request) { fruits.add(new Fruit(request.getName(), request.getWarehousingDate(), request.getPrice())); } @Override public void saleFruit(SoldOutFruitRequest request) { Fruit fruit = fruits.get((int) request.getId()); fruit.setSold(true); } @Override public boolean checkSoldOut(SoldOutFruitRequest request) { Fruit fruit = fruits.get((int) request.getId()); if (fruit.isSold()) { return true; } return false; } @Override public long salesRead(String name) { long price = 0; for (int i = 0; i 결과)메모리에 저장하는 방식으로서, 서버를 재시작 할때마다 데이터를 입력해 주어야 한다. 초기 데이터 입력으로 사과 5000, 사과 7000, 사과 9000을 입력해주었고, 사과 7000을 판매했을 때 판매 데이터 결과값이다. FruitMySqlRepository)@Repository public class FruitMySqlRepository implements FruitRepository { private final JdbcTemplate jdbcTemplate; public FruitMySqlRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void saveFruit(FruitRequest request) { String sql = "insert into fruit (name, warehousing_date, price) values (?, ?, ?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); } @Override public void saleFruit(SoldOutFruitRequest request) { String sql = "update fruit set is_sold = true where id = ?"; jdbcTemplate.update(sql, request.getId()); } @Override public boolean checkSoldOut(SoldOutFruitRequest request) { String readSql = "select id from fruit where id = ? and is_sold = false"; return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty(); } @Override public long salesRead(String name) { String salesRead = "select sum(price) price from fruit where name = ? and is_sold = true"; List price = jdbcTemplate.query(salesRead, (rs, rowNum) -> rs.getLong("price"), name); return price.get(0); } @Override public long notSalesRead(String name) { String notSalesRead = "select price from fruit where name = ? and is_sold = false group by price"; List price = jdbcTemplate.query(notSalesRead, (rs, rowNum) -> rs.getLong("price"), name); return price.get(0); } }계층구조 리팩토링을 진행한 결과로서, 4일차 과제와 동일한 결과값을 나타내주었다.이번 리팩토링을 진행함으로서 controller, service, repository 계층별 역할과 책임에 대해 자세히 알게 되었고, 인터페이스를 적용시킬때가 언제 인지를 확실하게 알게 되었다. 추가적으로, FruitMemoryRepository에서 스트림, 람다식, 메소드 레퍼런스를 사용해보았다. @Override public long salesRead(String name) { return fruits.stream() .filter(fruit -> fruit.getName().equals(name)) .filter(Fruit::isSold) .mapToLong(Fruit::getPrice) .sum(); } @Override public long notSalesRead(String name) { return fruits.stream() .filter(fruit -> fruit.getName().equals(name)) .filter(fruit -> !fruit.isSold()) .mapToLong(fruit -> fruit.getPrice()) .sum(); }salesRead에서 메소드 레퍼런스를 적용시켰고, notSalesRead에서는 람다식을 이용해서 코드를 작성해보았다.3일차 과제에서 배웠던 내용을 적용시킴으로 한층 더 발전된 코드가 되었다.
![[인프런 워밍업 클럽 0기] BE 1주차 발자국](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 25.
0
[인프런 워밍업 클럽 0기] BE 1주차 발자국
신청 이유스프링 공부를 했지만 내가 스프링을 잘 다룰 수 있을까? 라는 생각이 들던차에, 이번 워밍업 클럽을 보게 됐습니다. 제가 가지고 있는 지식을 활용하여 커리큘럼을 따라가고, 따라가는 과정에서 주어지는 과제를 직접 코드로 구현하고, 이론적인 내용을 학습하기 위해 신청을 하게 됐습니다. 일주일 간의 학습내용GET, POST, PUT API 만들기Database 테이블 생성 및 테이블 수정클린코드1일차: 서버 개발을 위한 환경 설정 및 네트워크 기초스프링 부트 초기설정 방법에 대해 공부하고, HTTP에 대해 간략하게 학습하였다. 2일차: 첫 HTTP API 개발GET,POST API를 이용해 유저 생성 및 유저 조회 API를 개발하였다.3일차: 기본적인 데이터베이스 사용법유저 리스트를 휘발성 메모리에 담아 서버가 꺼지면 없어지는 방식을 데이터베이스를 이용해 서버가 꺼지더라도 비휘발성 메모리에 담게 저장방식을 바꾸었다.4일차: 데이터베이스를 사용해 만드는 API유저 업데이트 API, 유저 삭제 API 개발과 예외 처리를 진행하였다.5일차: 클린코드의 개념과 첫 리팩토링프로젝트의 유지보수를 위해서 클린코드는 필수적이다. 클린코드란 무엇이고 기존의 Controller를 Service와 Repository 계층으로 나누어 리팩토링 하였다. 1주차 미션1일차: 어노테이션 이란?https://www.inflearn.com/blogs/65402일차: GET, POST API 개발https://www.inflearn.com/blogs/65783일차: 람다식, 익명클래스, 함수형 프로그래밍 이란?https://www.inflearn.com/blogs/66484일차: 데이터베이스를 이용한 API 개발https://www.inflearn.com/blogs/66815일차: 클린코드를 위한 코드 리팩토링https://www.inflearn.com/blogs/6709회고1주차를 진행하면서, 나에게 어떤 부분이 부족했는지 더욱 와닿는 시간을 가졌다. 과제를 직접 개발해보고, 더 좋은 방법은 없을까 생각을 하면서 진행했던 부분이 다양한 방법을 생각하게 해주었으며 람다와 스트림과 같은 함수형 프로그래밍과 같은 기법도 조금 더 연습이 필요 하다는 것을 알게 되었다. 차주 강의에서도 이러한 부분에 대해 고민하며, 과제를 수행할 시 부족한 부분에 대해 더 학습하여 내 것으로 만든다는 느낌을 가지고 진행해 보도록 하겠습니다.
![[인프런 워밍업 클럽] BE 0기 5일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 23.
0
[인프런 워밍업 클럽] BE 0기 5일차 과제
클린코드를 위해 각 책임에 맞게 객체들을 만들어 보았다. Main, InputOutput, CountingMainpublic class Main { public static void main(String[] args) { InputOutput io = new InputOutput(); Counting count = new Counting(); int a = io.numberInput(); count.count(a); io.output(count); } }InputOutputimport java.util.Scanner; public class InputOutput { public int numberInput(){ System.out.print("숫자를 입력하세요 : "); Scanner scanner = new Scanner(System.in); return scanner.nextInt(); } public void output(Counting counting) { for (int i = 0; i Countingpublic class Counting { public final static int DICENUM = 6; private int[] arr = new int[DICENUM]; public void count(int a) { for (int i = 0; i 입출력을 위한 객체를 만들어 입출력만을 위한 책임을 부여했으며, Counting 객체를 만들어 배열을 만든 뒤, 나온 숫자에 따라 횟수를 증가시켰으며, 원하는 숫자의 횟수가 몇 번인지 알기 위해 getter를 수정하였습니다. 현재 Counting 객체는 DICENUM 상수를 통하여 간단한 변수설정을 위해 주사위 면의 갯수를 설정하고, 그에 따라 배열 설정과 출력이 가능하게 하였습니다. 하지만 이 방법은 여러 면의 주사위를 사용하거나, 전략패턴을 사용할 때와 같이, 몇 개의 면을 가진 주사위를 사용할지 런타임에 결정되게 할 때에는 적합하지 않습니다.위와 같은 문제의 해결 방법으로 생각해본 것은, 인터페이스를 만들어 count 메소드를 선언하며, 각 구현체를 주사위의 면을 사용할 개수만큼 만들어 구현하여 기존에는 불가능하였던 확장이 가능해집니다. 하지만 단점도 존재합니다. 초기 코드를 작성할 때 여러개의 Counting 구현체를 만들어야 하여 코드를 작성하는 것이 번거로워 프로젝트의 크기를 생각할 때 trade-off를 생각하면, 인터페이스를 만든 뒤 구현체를 만드는 방식보단, 상수설정을 통하여 한개의 변수를 건드는 것 만으로 원하는 대로 기능이 작동되게 하였습니다. 주사위 면이 6개일 때의 결과 (DICENUM = 6)주사위 면이 12개일 때의 결과 (DICENUM = 12)
![[인프런 워밍업 클럽] BE 0기 4일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 22.
0
[인프런 워밍업 클럽] BE 0기 4일차 과제
컨트롤러)@RestController public class WorkController { private JdbcTemplate jdbcTemplate; public WorkController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @PostMapping("/api/v1/fruit") public void saveFruit(@RequestBody FruitRequest request) { String sql = "insert into fruit (name, warehousing_date, price) values (?, ?, ?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); } } fruit Table 생성)create table fruit( id bigint auto_increment, name varchar(30), warehousing_date date, price bigint, primary key (id) ); POST요청 Body를 받기 위한 FruitRequestDTO 생성)import java.time.LocalDate; public class FruitRequest { private String name; private LocalDate warehousingDate; private long price; public String getName() { return name; } public LocalDate getWarehousingDate() { return warehousingDate; } public long getPrice() { return price; } } 결과)정상적으로 데이터베이스에 저장된 것을 확인 할 수 있었다. int type은 4바이트로서 최대 정수의 범위가 약 21억까지밖에 표현을 할 수 없기 때문에, 가격의 총 합과 같은 경우를 생각해본다면, 21억이 넘는 숫자는 빈번히 발생한다. 그렇기 때문에 8바이트인 long type을 사용하여 오버플로우를 방지하기 위해 사용한 것 이라고 생각한다. 기존 테이블에는 품절 여부를 확인할 수 있는 속성이 없었기 때문에 속성을 추가해주었다.alter table fruit add column is_sold boolean default false;컨트롤러)public class WorkController { private JdbcTemplate jdbcTemplate; public WorkController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @PutMapping("/api/v1/fruit") public void saleFruit(@RequestBody SoldOutFruitRequest request) { String readSql = "select id from fruit where id = ? and is_sold = false"; boolean isEmpty = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty(); if (isEmpty){ throw new IllegalArgumentException(); } String sql = "update fruit set is_sold = true where id = ?"; jdbcTemplate.update(sql, request.getId()); } } 이미 품절 되었나 확인해보기 위해 조건을 확인하는 read 쿼리를 만들어 확인 후 이미 팔렸다면 예외 처리를 해주었다. BODY 데이터를 받기 위한 SoldOutFruitRequest)public class SoldOutFruitRequest { private long id; public long getId() { return id; } } 결과)컨트롤러)@RestController public class WorkController { private JdbcTemplate jdbcTemplate; public WorkController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @GetMapping("/api/v1/fruit/stat") public TotalPriceResponse salesData(@RequestParam String name) { String salesRead = "select price from fruit where name = ? and is_sold = true"; List price = jdbcTemplate.query(salesRead, (rs, rowNum) -> rs.getLong("price"), name); String notSalesRead = "select price from fruit where name = ? and is_sold = false"; List price1 = jdbcTemplate.query(notSalesRead, (rs, rowNum) -> rs.getLong("price"), name); return new TotalPriceResponse(price.stream().mapToLong(i -> i).sum(), price1.stream().mapToLong(i -> i).sum()); } } 팔린 품목들의 합과 안팔린 품목들의 합을 구하기 위해 read 쿼리를 두번 날려보았다.이후 출력을 위해 Response객체를 만들어주었다.TotalPriceResponse)public class TotalPriceResponse { private long salesAmount; private long notSalesAmount; public TotalPriceResponse(long salesAmount, long notSalesAmount) { this.salesAmount = salesAmount; this.notSalesAmount = notSalesAmount; } public long getSalesAmount() { return salesAmount; } public long getNotSalesAmount() { return notSalesAmount; } }결과)추가적으로 DB에서 처리하여 가져올 수 있도록 sum과, group by를 써서 만들어 보았다. @GetMapping("/api/v1/fruit/stat") public TotalPriceResponse salesData(@RequestParam String name) { String salesRead = "select sum(price) price from fruit where name = ? and is_sold = true"; List price = jdbcTemplate.query(salesRead, (rs, rowNum) -> rs.getLong("price"), name); String notSalesRead = "select price from fruit where name = ? and is_sold = false group by price"; List price1 = jdbcTemplate.query(notSalesRead, (rs, rowNum) -> rs.getLong("price"), name); return new TotalPriceResponse(price.get(0), price1.get(0)); } 팔린 품목에 대해서는 sum 함수를, 안팔린 품목에 대해서는 group by절을 추가하여 사용한 형태이다.다만 where절의 항목에 없을경우 예외처리를 해줘야 하는 부분이 있다.
백엔드
・
인프런워밍업클럽
![[인프런 워밍업 클럽] BE 0기 3일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 21.
0
[인프런 워밍업 클럽] BE 0기 3일차 과제
자바의 람다식은 왜 등장했을까?프로그래밍 패러다임은 시간이 지남에 따라 계속 발전해오고 있다. 최근의 프로그래밍 패러다임은 명령형 프로그래밍과 선언형 프로그래밍으로 구분되고 있다. 절차지향과 객체지향 같은 명령형 프로그래밍은 개발하는 소프트웨어의 크기가 커짐에 따라, 스파게티 코드를 유지보수하기 매우 힘들단 것을 깨닫게 되었고, 이를 해결하기 위해 함수형 프로그래밍이라는 프로그래밍 패러다임에 관심을 갖게 되었다. 함수형 프로그래밍은 거의 모든 것을 순수 함수로 나누어 문제를 해결하는 방식으로, 작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 해준다.이와 같은 흐름에 따라, 자바 진영도 jdk8부터 람다식, Stream API, Functinal Interface와 같은 함수형 프로그래밍을 지원하기 시작했다. 람다식이 도입됨에 따라 함수형 프로그래밍의 장점을 가져왔으며 그 장점인 코드의 효율과 가독성의 향상을 가져왔다. 람다식과 익명 클래스는 어떤 관계가 있을까? - 람다식의 문법은 어떻게 될까? 익명 클래스(Anonymous Class)프로그램에서 일시적으로 한번만 사용되고 버려지는 객체다. 프로그램 내에서 일시적으로 사용되는 객체 처리에 사용한다.확장성을 활용하는 것이 유지보수에서 더 불리할 경우에 사용한다.예시)// Animal이라는 부모 클래스 class Animal { public String bark() { return "동물이 우는 소리"; } } public class Main { public static void main(String[] args) { // 객체 생성과 동시에 클래스 정의 Animal dog = new Animal() { @Override public String bark() { return "개가 짖는 소리"; } }; // 익명 클래스의 끝에 세미콜론을 반드시 붙여 주어야 한다. dog.bark(); } }람다(Lambda)람다식은 메소드를 하나의 식으로 표현한 것이다. 익명 클래스와 비슷한 개념이지만 코드는 훨씬 간결하다. 표현식은 다음과 같다. 예시) List words = new ArrayList(); words.add("apple"); words.add("banana"); words.add("cherry"); // words를 sort하는 class method Collections.sort(words, new Comparator() { // 익명 클래스 정의 public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }); for (String word : words) { System.out.println(word); }위와 같은 익명클래스를 람다식으로 변환)Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));람다식을 메서드 레퍼런스로 변환)Collections.sort(words, comparingInt(String::length));람다(Lambda) 사용 시 주의할 점1. 람다는 이름이 없고 문서화도 불가능하므로, 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 쓰지 말아야 한다.코드 줄 수가 많다는 기준 - 한 줄이 가장 좋고 길어야 세 줄!2. 람다가 익명 클래스보다 훨씬 간결한 것은 맞지만, 대체 불가능한 경우가 있다.람다는 함수형 인터페이스에서만 쓰이므로, 추상 클래스의 인스턴스를 만들 때는 익명 클래스를 써야 한다.람다는 자기자신을 참조할 수 없다.(this X) 함수 객체가 자신을 참조해야 한다면 익명 클래스를 써야 한다.람다를 직렬화하는 일은 극히 삼가야 한다. 1. 익명 내부 클래스는 새로운 클래스를 생성하지만, 람다는 새로운 메서드를 생성하여 포함한다.람다는 static 이든, 객체 사용을 위한 non-static 이든, 메서드로 생성된다.이에 반해 익명 내부 클래스는 새로운 클래스파일이 생성된다. 2. 익명 내부 클래스의 this : 새로 생성된 클래스, 람다의 this : 람다식을 포함하는 클래스위와 완벽하게 일맥상통하는 말이다.새로운 클래스를 만들기 때문에, 익명 내부클래스에서의 this 는 해당 클래스를 가리킨다.새로운 클래스를 만드는 게 아니고 그냥 메서드를 만드는 람다는, 메서드 즉 람다가 있는 클래스를 가리킨다. 출처) 이펙티브자바,https://mangkyu.tistory.com/111,https://cobinding.tistory.com/213
백엔드
・
인프런워밍업클럽
![[인프런 워밍업 클럽] BE 0기 2일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 20.
0
[인프런 워밍업 클럽] BE 0기 2일차 과제
컨트롤러 코드)@RestController public class WorkController { @GetMapping("/api/v1/calc") public ResultResponse result(NumberRequest request) { ResultResponse response = new ResultResponse(request.addNum(), request.minusNum(), request.multiplyNum()); return response; } }DTO를 만들어 객체 내부에서 일을 수행하게 하고, 코드의 간결성을 높였습니다. Request DTO)public class NumberRequest { private int num1; private int num2; public NumberRequest(int num1, int num2) { this.num1 = num1; this.num2 = num2; } public int getNum1() { return num1; } public int getNum2() { return num2; } public int addNum(){ return num1 + num2; } public int minusNum(){ return num1 - num2; } public int multiplyNum(){ return num1 * num2; } }request dto에서 객체 스스로 일을 처리하게 하여 능동적인 객체를 만들었습니다. response DTO)public class ResultResponse { private int add; private int minus; private int multiply; public ResultResponse(int add, int minus, int multiply) { this.add = add; this.minus = minus; this.multiply = multiply; } public int getAdd() { return add; } public int getMinus() { return minus; } public int getMultiply() { return multiply; } }JSON 결과값을 내놓기 위해 response DTO 객체를 만들어 반환해주었습니다.결과)정상적으로 수행하는 모습을 확인할 수 있습니다. 컨트롤러)@RestController public class WorkController { @GetMapping("/api/v1/day-of-the-week") public DateResponse result(@RequestParam String date) { return new DateResponse(date); } }response DTO)import java.time.DayOfWeek; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class DateResponse { private String dayOfTheWeek; public DateResponse(String dayOfTheWeek) { this.dayOfTheWeek = translate(dayOfTheWeek); } public String getDayOfTheWeek() { return dayOfTheWeek; } public String translate(String date){ DayOfWeek dayOfWeek = LocalDate.parse(date, DateTimeFormatter.ISO_DATE).getDayOfWeek(); return String.valueOf(dayOfWeek).substring(0, 3); } } 쿼리의 입력값을 String 형식으로 받은 후 DTO에서 LocalDate를 이용해 변환 작업을 거쳤다.컨트롤러에서 처리할 내용을 객체에서 처리하게 하여 코드의 간결함을 향상 시켰다.DateTimeFormatter.ISO_DATE: "yyyy-MM-dd" 형식으로 포맷팅 해주는 상수이다. 결과) 컨트롤러)@RestController public class WorkController { @PostMapping("/api/v1/sum") public int result(@RequestBody ListRequest request) { return request.getSum(); } } requestDTO)import java.util.List; public class ListRequest { private List numbers; public List getNumbers() { return numbers; } public int getSum() { return numbers.stream().mapToInt(i -> i).sum(); } }@RequestBody가 붙은 dto에서는 반드시 빈 생성자가 있어야 한다 라는 것을 알게 되었다. 이유를 찾아보았더니,ObjectMapper에서는 객체의 상태를 초기화하기 위해 빈 생성자를 사용하고, 빈 생성자가 선언되어 있지 않다면 런타임 에러가 발생한다. 따라서 HTTP 요청으로 JSON 형태의 Request Body를 받는 경우, DTO에 빈 생성자가 있지 않다면 DTO 객체를 생성하지 못해 에러가 발생한다. 라는 오라클의 공식문서에 나와있었다.ObjectMapper)"ObjectMapper는 기본 POJO(Plain Old Java Objects) 또는 범용 JSON Tree Model(JsonNode)에서 JSON을 읽고 쓰는 기능과 변환 수행을 위한 기능을 제공합니다.또한 다양한 스타일의 JSON 컨텐츠와 함께 작동하고 다형성 및 객체 동일성과 같은 고급 객체 개념을 지원하도록 Customizing할 수 있습니다."결과)
백엔드
・
인프런워밍업클럽
![[인프런 워밍업 클럽] BE 0기 1일차 과제](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 02. 19.
0
[인프런 워밍업 클럽] BE 0기 1일차 과제
자바를 사용하다보면 메서드나 클래스명 위에 @가 붙은 것들을 많이 보았을 것 이다. ex) @OverrideOverride가 재정의라는것은 알고 있지만 도대체 @가 붙은 게 무엇이고 무슨 기능을 할까?지금부터 어노테이션에 대해 알아보도록 하자. 어노테이션 이란?어노테이션은 사전적 의미로는 주석이라는 뜻이다. 자바에서 사용될 때의 어노테이션은 코드 사이에 주석처럼 쓰여서 특별한 의미, 기능을 수행하도록 하는 기술이다. 즉, 프로그램에게 추가적인 정보를 제공해주는 메타데이터(meta data: 데이터를 위한 데이터)라고 볼 수 있다. 어노테이션을 사용하는 이유 (효과) 는 무엇일까? 컴파일러에게 문법 에러를 체크하도록 정보를 제공한다. 프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다. 런타임에 특정 기능을 실행하도록 정보를 제공한다. 어노테이션은 @를 사용하여 작성하며, 해당 타겟에 대한 동작을 수행하는 프로그램 외에는 다른 프로그램에게 영향을 주지 않는다. 나만의 어노테이션은 어떻게 만들 수 있을까?커스텀 어노테이션을 만들 때 몇 가지 규칙이 있습니다. 어노테이션 타입은 @interface로 정의해야합니다. 모든 어노테이션은 자동적으로 java.lang.Annotation 인터페이스를 상속하기 때문에 다른 클래스나 인터페이스를 상속 받으면 안됩니다. 파라미터 멤버들의 접근자는 public이거나 default여야만 합니다. 파라미터 멤버들은 byte,short,char,int,float,double,boolean,의 기본타입과 String, Enum, Class, 어노테이션만 사용할 수 있습니다. 클래스 메소드와 필드에 관한 어노테이션 정보를 얻고 싶으면, 리플렉션만 이용해서 얻을 수 있습니다. 다른 방법으로는 어노테이션 객체를 얻을 수 없습니다. 최초 어노테이션 생성@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperationIcon { String strategy() default "+"; }@Target어노테이션을 어떤 것에 적용할지를 선언할 때 사용@Target()괄호 안에 적용 대상을 지정@Retention얼마나 오래 어노테이션 정보가 유지되는지 선언괄호 안에 적용 대상 지정어노테이션을 적용할 클래스public class CalculatorService { @OperationIcon public void add(int a, int b) { OperationIcon annotation; try { annotation = getClass().getMethod("add", int.class, int.class).getAnnotation(OperationIcon.class); System.out.println(a + " " + annotation.strategy() + " " + b); System.out.println("Result: " + (a + b)); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } @OperationIcon(strategy = "-") public void subtract(int a, int b) { OperationIcon annotation; try { annotation = getClass().getMethod("subtract", int.class, int.class).getAnnotation(OperationIcon.class); System.out.println(a + " " + annotation.strategy() + " " + b); System.out.println("Result: " + (a - b)); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } } Main 실행public class TestMain { public static void main(String[] args) { CalculatorService calculator = new CalculatorService(); calculator.add(5, 3); calculator.subtract(10, 4); } }




