블로그

이용수

[인프런 워밍업 클럽 1기] BE 5일차

[인프런 워밍업 클럽 1기] BE 5일차본 게시글은 다음 강의 내용을 진행하고 있습니다.자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] - https://inf.run/XKQg문제 1 풀이 과정, 한 걸음 더사용자로부터 입력을 받는 부분, 주사위를 던지고 계산하는 부분, 결과를 출력하는 부분으로 메소드를 분리하여 구현했다.숫자 범위가 달라지더라도 동작하도록 코드를 수정했다.DiceRoller.javapackage com.group.libraryapp.controller.assignment3; import java.util.Scanner; public class DiceRoller { public static void main(String[] args) { int numOfFaces = getNumOfFaces(); // 주사위 면의 수를 입력 받음 int[] faceCounts = rollDice(numOfFaces); // 주사위를 던져 각 숫자의 출현 횟수를 계산 printResult(faceCounts); // 결과 출력 } // 사용자로부터 주사위 면의 수를 입력받는 메소드 private static int getNumOfFaces() { System.out.println("주사위 면의 수를 입력하세요:"); Scanner scanner = new Scanner(System.in); return scanner.nextInt(); } // 주사위를 던져 각 숫자의 출현 횟수를 계산하는 메소드 private static int[] rollDice(int numOfFaces) { int[] faceCounts = new int[numOfFaces]; for (int i = 0; i < numOfFaces; i++) { double randomValue = Math.random() * numOfFaces; int face = (int) randomValue; faceCounts[face]++; } return faceCounts; } // 결과를 출력하는 메소드 private static void printResult(int[] faceCounts) { for (int i = 0; i < faceCounts.length; i++) { System.out.printf("%d은(는) %d번 나왔습니다.\n", i + 1, faceCounts[i]); } } }결과 - 6 입력결과 - 20 입력

백엔드백엔드인프런워밍업

이슬

인프런 워밍업 클럽 스터디 1기 FE 과제(1번, 2번, 3번 과제)

[1번 과제(Day2) - 음식 메뉴 앱]따라하며 배우는 자바스크립트 A-Z학습 범위: Section 1 ~ 3https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/food-menu-app과제 이미지데이터를 json파일로 만들어놓고 처음 로드할 때, 불러와서 작업했다.강의에서 배운 이벤트위임을 활용해서 버튼 그룹에 이벤트리스너를 설정해주고 이벤트타겟의 태그가 버튼일 때에만 메뉴를 필터링할 수 있는 함수를 실행하게 했다.[2번 과제(Day3) - 가위 바위 보 앱]따라하며 배우는 자바스크립트 A-Z학습 범위: Section 4(1~8)https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/rock-scissors-paper-app과제 이미지남은 횟수가 끝나면 결과를 알려주는 부분을 잊고 완성했다가 다시 이어서 작업했다.다시 도전하기를 누를때 화면을 초기화하는 함수를 만들었다.[3번 과제(Day4) - 퀴즈 앱]따라하며 배우는 자바스크립트 A-Z학습 범위: Section 4(9~17)https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/js-quiz-app과제 이미지1번 처럼 데이터를 json파일로 만들어놓고 처음 로드할 때, fetch로 불러와서 작업했는데 데이터 객체 안에 문제와 옵션들, 그리고 answer(정답) 키값을 넣었었다. 하지만 브라우저 네트워크에 답까지 노출이 되는 게 문제라고 생각됐다.그래서 answer(정답) 키값을 없애고, 문제와 옵션들만 데이터로 구성하고 script 안에서 정답 유무를 가릴 수 있게 바꿨다. 풀이는 문제를 배열로 해체하고 ×를 *으로, ÷를 /으로 바꾼 후(-,+는 그대로) eval()함수를 쓰지않고 new Function() 새로운 함수를 생성해서 풀이하게 했다. 

웹 개발인프런워밍업클럽FE1기과제

mingle

[인프런 워밍업 클럽 스터디 BE 1기] 첫 번째 발자국

늦었지만 올려보는 첫 번째 발자국참여 계기취업 한지 벌써 2년이 되어간다. 일을 할수록 부족한 나를 마주하는 순간이 많았다. 그래서 항상 공부하겠다고 다짐했지만, 퇴근 후 공부는 생각보다 쉽지 않았고…. ㅎㅎ.. 그런 와중에 인프런에 들어왔다가 워밍업 클럽 스터디 배너를 보고 관심이 갔다. BE 강의 커리큘럼을 보니 회사에서 사용하지 않는 기술도 있어 더 흥미가 갔고 공부하기 좋은 기회라고 생각해서 지원하게 됐다. 배운 내용환경 세팅, HTTPHTTP Method(GET, POST, PUT, DELETE)를 활용한 간단한 API 개발MySQL과 DDL, DMLSpring과 DB 연결. CRUD API 개발.관심사의 분리 가장 인상 깊은 강의는 리팩토링이다. 회사 일을 하다가 내가 짠 코드에 대해 다른 직원분으로부터 문의를 받은 적이 있다. 시간에 쫓겨 작성한 부분이었고.. 강의를 들으면서 매우 많이 찔렸다. 아무리 바빠도 이후에 내 코드를 읽을 동료, 나를 위한 개발을 해야겠다고 생각했다. 화이팅~스터디에서 가장 만족하는 점은 과제다. 과제를 하다 보면 관련된 다른 궁금한 점이 생기고, 계속 샛길로 빠지게 된다. 한 과제를 하는데 하루 종일 걸린다ㅎㅎ.. 기한이 2일이라서 다행이다. 과제 덕분에 더 많이 배울 수 있었고 재밌었다. 이번 주에 배울 내용과 할 과제도 기대가 된다. 과제

백엔드BE워밍업1기

xicodey

[인프런 워밍업 클럽 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 < a; i++) { double b = Math.random() * 6; if (b >= 0 && b < 1) { r1++; } else if (b >= 1 && b < 2) { r2++; } else if (b >= 2 && b < 3) { r3++; } else if (b >= 3 && b < 4) { r4++; } else if (b >= 4 && b < 5) { r5++; } else if (b >= 5 && b < 6) { r6++; } } System.out.printf("1은 %d번 나왔습니다.\n", r1); System.out.printf("2은 %d번 나왔습니다.\n", r2); System.out.printf("3은 %d번 나왔습니다.\n", r3); System.out.printf("4은 %d번 나왔습니다.\n", r4); System.out.printf("5은 %d번 나왔습니다.\n", r5); System.out.printf("6은 %d번 나왔습니다.\n", r6); } } 제시된 코드를 최대한 클린하게 만들어라 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 <= rolls; i++) { int number = (int)(Math.random() * sides) + 1; results[number]++; } return results; } private static void resultPrint(int sides, int[] results) { for (int i = 1; i <= sides; i++) { System.out.printf("%d은 %d번 나왔습니다.\n", i, results[i]); } } }주사위 면을 받을 수 있는 inputSide함수와 몇번 주사위를 굴릴건지 입력을 받을 수 있는 inputDice 함수,주사위를 돌려 결과를 저장하는 rollTheDice함수와 결과를 출력하는 resultPrint 함수로 구성했다.각 변수는 의미있는 변수명으로 바꾸고 메서드명도 그에 맞게 바꾸었다.

인프런워밍업클럽스터디1기백엔드

이용수

[인프런 워밍업 클럽 1기] BE 4일차

[인프런 워밍업 클럽 1기] BE 4일차본 게시글은 다음 강의 내용을 진행하고 있습니다.자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] - https://inf.run/XKQg문제 1구현 과정구현 목표 : 과일 정보를 입력해 요청하면 상태코드 반환하여 응답함.메서드 타입 : POST경로 : /api/v1/fruitHTTP 요청 Body : { "name" : String, "warehousingDate" : LocalDate, "price" : long } SQL 쿼리문create table fruit ( id bigint auto_increment, name varchar(30), warehousingDate date, price bigint, status boolean default false, primary key (id) );FruitController.javapackage com.group.libraryapp.controller.assingment2.fruit; import com.group.libraryapp.dto.assignment2.request.FruitRequest; import com.group.libraryapp.dto.assignment2.request.SoldRequest; import com.group.libraryapp.dto.assignment2.response.SalesSumResponse; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.*; @RestController public class FruitController { private final JdbcTemplate jdbcTemplate; public FruitController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @PostMapping("/api/v1/fruit") public void saveFruit(@RequestBody FruitRequest request) { String sql = "INSERT INTO fruit (name, warehousingDate, price) VALUES (?, ?, ?)"; jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice()); }FruitRequest.javapackage com.group.libraryapp.dto.assignment2.request; import java.time.LocalDate; public class FruitRequest { private long id; private String name; private LocalDate warehousingDate; private long price; public long getId() { return id; } public String getName() { return name; } public LocalDate getWarehousingDate() { return warehousingDate; } public long getPrice() { return price; } }한 걸음 더 API에서 long을 사용한 이유null을 사용할 수 있다.int를 사용할 경우 기본값이 0이여서 값이 없어서 0으로 초기화 된건지, 실제 값이 0인지 데이터만 보고 판별하기 어려운 문제점이 있다.반면 Long을 사용할 경우 값이 없으면 null로 초기화되고, 실제 값이 0이면 0으로 저장되기 때문에 값의 유무를 쉽게 판별할 수 있다는 점에서 Long을 주로 사용한다.int보다 더 넓은 범위의 값을 제공한다.정수형 타입 : 데이터의 표현 범위Int : -2,147,483,648 ~ 2,147,483,647Long : -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807대규모 데이터를 처리하는 경우 데이터의 범위가 중요한데, long 자료형은 int 자료형 보다 월등히 더 넓은 범위의 값을 제공한다.문제 2구현 과정구현 목표 : id를 입력해 요청하면 상태코드 반환하여 응답함.메서드 타입 : PUT경로 : /api/v1/fruitHTTP 요청 Body : { "id" : long }SoldRequest.javapackage com.group.libraryapp.dto.assignment2.request; public class SoldRequest { private long id; public long getId() { return id; } }++FruitController.java ... @PutMapping("/api/v1/fruit") public void updateFruit(@RequestBody SoldRequest request) { String readSql = "SELECT * FROM fruit WHERE id = ?"; boolean isFruitNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty(); if (isFruitNotExist) { throw new IllegalArgumentException(); } String updateSql = "UPDATE fruit SET is_sold = true WHERE id = ?"; jdbcTemplate.update(updateSql, request.getId()); } ...문제 3구현 과정구현 목표 : 과일 정보를 입력해 요청하면 팔린 금액, 팔리지 않은 금액을 반환하여 응답함.메서드 타입 : GET경로 : /api/v1/fruit/statHTTP 요청 Body : { "salesAmount" : long, "notSalesAmount" : long }SalesSumResponse.javapackage com.group.libraryapp.dto.assignment2.response; public class SalesSumResponse { private long salesAmount; private long notSalesAmount; public SalesSumResponse(long salesAmount, long notSalesAmount) { this.salesAmount = salesAmount; this.notSalesAmount = notSalesAmount; } public long getSalesAmount() { return salesAmount; } public long getNotSalesAmount() { return notSalesAmount; } }++FruitController.java@GetMapping("/api/v1/fruit/stat") public SalesSumResponse getAmountInfo(@RequestParam String name) { String sql = "SELECT * FROM fruit WHERE name = ?"; List<SumResponse> list = jdbcTemplate.query(sql, (rs, rowNum) -> new SumResponse(rs.getLong("price"), rs.getBoolean("status")), name); long salesAmount = list.stream() .filter(SumResponse::status) .mapToLong(SumResponse::getPrice) .sum(); long notSalesAmount = list.stream() .filter(res -> !res.status()) .mapToLong(SumResponse::getPrice) .sum(); return new SalesSumResponse(salesAmount, notSalesAmount); }한 걸음 더sum, group by 키워드를 사용해 구현하기++FruitController.java... @GetMapping("/api/v1/fruit/stat") public SalesSumResponse statFruit(@RequestParam String name) { String salesSql = "SELECT sum(price) FROM fruit WHERE is_sold = true GROUP BY name HAVING name = ?"; String notSalesSql = "SELECT sum(price) FROM fruit WHERE is_sold = false GROUP BY name HAVING name = ?"; long salesAmount = jdbcTemplate.queryForObject(salesSql, long.class, name); long notSalesAmount = jdbcTemplate.queryForObject(notSalesSql, long.class, name); return new SalesSumResponse(salesAmount, notSalesAmount); } ...JPA Entity클래스에서 id를 int가 아닌 Long 타입으로 하는 이유 (tistory.com)

백엔드백엔드인프런워밍업

준섭

인프런 워밍업 클럽 스터디 1기 BE 4번째 과제

문제1과일 정보 저장 API{ "name": "사과", "warehousingDate": "2024-02-01", "price": 5000 } 위와 같은 정보를 받아서 저장할 수 있도록 fruit 테이블을 만들었다.create table fruit ( id bigint auto_increment, name varchar(30), warehousingdate date, price bigint, primary key(id) ); 그리고 이전에 구현했던 내용과 똑같이 INSERT문을 이용해 과일 정보 저장 API를 구현했다.public void save(Fruit fruit) { String sql = "INSERT INTO fruit (name, warehousingdate, price) VALUES (?, ?, ?)"; jdbcTemplate.update(sql, fruit.getName(), fruit.getWarehousingDate(), fruit.getPrice()); } 가격을 나타내는 price의 테이터 타입은 long을 사용했는데int를 사용하지 않고 long을 사용하는 이유는가격의 크기가 int의 범위를 초과할 수 있기 때문이다.문제2문제2는 과일이 팔린 경우 팔렸다는 정보를 저장하는 API를 구현하는 문제팔렸다는 정보를 저장하기 위해서 fruit 테이블에 sold라는 컬럼을 추가했다.create table fruit ( id bigint auto_increment, name varchar(30), warehousingdate date, price bigint, sold boolean default false, primary key(id) ); boolean 타입이고 default 값은 false인 sold 컬럼을 만들어서과일이 팔린 경우 해당 API를 호출하면 sold 컬럼을 true로 바꿔주도록 구현헀다.public void updateSold(Long id) { String sql = "UPDATE fruit SET sold = true WHERE id = ?"; jdbcTemplate.update(sql, id); } 문제3특정 과일의 팔린 금액과 팔리지 않은 금액을 조회하는 API 구현하기2가지의 방법으로 구현할 수 있다.1. 과일의 이름에 해당하는 모든 데이터를 조회해서 Service 단에서 금액을 계산하기FruitRepositorypublic List<Fruit> getStat(String name) { String sql = "SELECT name, price, sold FROM fruit WHERE name = ?"; return jdbcTemplate.query(sql, (rs, rowNum) -> new Fruit( rs.getString("name"), rs.getLong("price"), rs.getBoolean("sold") ), name); } FruitServicepublic FruitStatResponse getStat(String name) { List<Fruit> fruits = fruitRepository.getStat(name); long salesAmount = 0; long notSalesAmount = 0; for (Fruit fruit : fruits) { if (fruit.getSold()) { salesAmount += fruit.getPrice(); } else { notSalesAmount += fruit.getPrice(); } } return new FruitStatResponse(salesAmount, notSalesAmount); } 데이터가 다음과 같이 들어있을 경우원하는 결과를 조회할 수 있다.2. 팔린 금액과 팔리지 않은 금액을 조회하는 쿼리를 만들기public FruitStatResponse getStat(String name) { String sql = "SELECT SUM(CASE WHEN sold = true THEN price ELSE 0 END) salesamount, " + "SUM(CASE WHEN sold = false THEN price ELSE 0 END) notsalesamount " + "FROM fruit WHERE name = ?"; return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> new FruitStatResponse( rs.getLong("salesamount"), rs.getLong("notsalesamount") ), name); } SUM 함수를 사용해서 금액의 합을 계산할 수 있고CASE문을 사용하면 sold가 true인 금액을 조건으로 해서 계산할 수 있다. 이렇게 쿼리를 이용하면 다양한 조건의 데이터를 검색할 수 있기 때문에Service단에서 원하는 데이터를 분류하는 작업을 하지 않을 수 있다.SQL을 잘 알면 활용할 수 있는 방법이 무궁무진 하지만SQL도 알면 알수록 더 어려워지는 것 같다…

백엔드

김설하

인프런 워밍업 클럽 스터디 1기 FE | 미션1, 미션2 발자국

미션1: 음식 메뉴 앱간단한 음식 목록을 카테고리 별로 분류하여 보여주는 앱📇 레포지토리 주소: https://github.com/zldnlto/inflearn-warming-up/tree/main/01_%EC%9D%8C%EC%8B%9D%20%EB%A9%94%EB%89%B4%20%EC%95%B1header, main과 같은 큰 골격을 index.html에 짜 두고 기능이 필요한 요소들은 동적으로 생성하여 DOM에 붙이는 방식으로 구현하였다.데이터 페칭을 다루는 방식이 미숙하다고 생각하여, 짧은 데이터지만 굳이 data.json 을 생성해서 페칭을 모사해보았다. HTML validator 통과, 카드 부분에 <figure>,<figcaption> 사용하는 등 시멘틱한 태그 작성 주요 로직MenuList 라는 객체 배열을 통해 메뉴 버튼을 생성하고 관리한다.init() 함수 는 앱 최초 진입시 실행되며 전체 카드 목록을 불러온다. ('All'이 디폴트) renderCard() 함수는 html 요소 템플릿을 찍어내는 함수다. 각각의 Card 컴포넌트를 생성한다.handleFilterBtn() 을 통해 버튼 id와 일치하는 foodData를 필터링한다.const renderCard = (data) => { let newCard = ""; if (data) { data.forEach((v) => { const menuItem = `<li class="card"> <figure class="card-content"> <img src="./assets/${v.image}" alt="${v.name}" class="card-cover" /> <figcaption class="card-text"> <div class="card-title"> <strong>${v.name}</strong> <div class="price">${v.price}원</div> </div> <span class="hr"></span> <p class="card-info"> ${v.description} </p> </figcaption> </figure> </li>`; newCard += menuItem; }); } return newCard; };각각의 요소를 따로 생성하기보다는 Card컴포넌트를 생성할 때 <li> 태그 이하의 자식 요소들이 많은 점을 고려하여 innerHTML 로 덩어리 템플릿을 이어붙이고 렌더링 하는 방식으로 구현하였다. 아쉬운 점 / 느낀점 :오랜만에 vanillaJS를 다뤄서 그런지 파일명과 같은 작은 부분들에서 '이게 맞나' 하는 생각의 연속이었다.프레임워크/라이브러리를 주로 사용했다고 한들 간단한 기능 위주임에도 생각보다 시간이 걸려서 아쉽다. 기능 위주로 구현하면 된다고 하셨는데 이왕이면 하는 마음을 버릴 수가 없어서 디자인도 조금씩 고민하게 된다. 간단한 기능일텐데 코드가 생각보다 길다. 더 효율적으로 짜는 방법이 있을 것 같다. 미션2: 가위 바위 보 앱컴퓨터와 가위바위보!📇 레포지토리 주소: https://github.com/zldnlto/inflearn-warming-up/tree/main/02_%EA%B0%80%EC%9C%84%EB%B0%94%EC%9C%84%EB%B3%B4%20%EC%95%B1 미션 프로젝트들을 한 레포지토리에 관리하려 하는데 root 경로에 common 폴더를 만들고 전반적으로 임포트해 사용할 reset.css 파일을 생성하였다. 주요 로직가위바위보 게임의 특성상 -1,0,1 숫자를 이용한 Matrix를 생성하여 승패를 판가름하였다.const resultMatric = [ [0, -1, 1], // rock [1, 0, -1], // scissors [-1, 1, 0], // paper ]; 기능 별 함수를 분리시켜서 캡슐화시키는 부분에 신경썼다.가위,바위,보 버튼을 눌렀을 때 화면 동작이 발생한다. 그런 의미에서 메인이 되는 함수를 뽑자면 각 버튼의 이벤트 함수라고 생각하였다. handleScissorBtn, handleRockbtn, handlePaperbtn 버튼을 보았을 때 어떤 동작이 일어나는지 한 번에 보이도록 하였고 코드는 아래와 같다. const handleScissorBtn = () => { USER_PICK = 1; COMPUTER_PICK = generateComputerPick(); calculateScore(USER_PICK, COMPUTER_PICK); resultRender(USER_SCORE, COMPUTER_SCORE); return USER_PICK; }; generateComputerPick() 0~2 사이의 난수를 리턴한다.calculateScore() 유저의 패와 컴퓨터의 패를 인자로 받아 resultMatric 기반으로 승패를 계산한다.resultRender() 승패 결과를 렌더링한다. 남은 횟수가 0이 되면 결과를 띄운다.  그 외 코드calculateResult() 게임이 끝난 후 유저가 이겼는지 컴퓨터가 이겼는지 결과만을 판단해 렌더링한다.아쉬운 부분const resultRender = (USER_SCORE, COMPUTER_SCORE) => { REMAINING_POINT -= 1; remainigPoint.innerText = REMAINING_POINT; userScore.innerText = USER_SCORE; computerScore.innerText = COMPUTER_SCORE; if (REMAINING_POINT === 0) { // 아쉬운 부분 resultText.innerText = ""; remainingBox.style.display = "none"; rcpBtnBox.style.display = "none"; selectText.innerText = calculateResult(); selectText.style.fontSize = "3.5rem"; restartBtn.style.display = "block"; return; } };남은 도전 횟수가 0이 되면 가위바위보 각 버튼과 선택하기 텍스트 등을 화면에서 숨기고 게임 결과와 재도전 버튼을 보여주어야 한다. 이 부분을 각각의 요소들을 일일이 display="none" css 처리를 하는 식으로 동작시키고 있는데, classList.add 메서드를 이용하여 코드를 줄이는 방식으로 리팩터링 할 수 있을 것 같다.또한 selectText 변수가 선택하기 텍스트를 보여주며 경우에 따라 게임 결과도 표현하고 있는데 변수명이 포괄적이지 못해 아쉽다.변수를 생성할 때는 영문 기반이라 바위-가위-보 순으로 생각하게 되는데 버튼은 가위 바위 보 순이어서 소소한 통일성 이슈가 있다. 우선 버튼 순서 빼고는 전자로 구현되어 있다. 버튼의 코드 정렬 순서와 가위바위보의 0,1,2 매칭 순서, 그리고 약어로 rsp인데 rcp라 오타나있는 부분 등 통일성 부분에서 개선이 필요하다. (rcpBtnBox) 느낀 점html 태그를 단순히 많이 생성하는 것을 지양하기 위해 body 태그에 id='root' 속성을 부여하고 DOM을 생성하려 했는데 body 안의 <script> 태그 때문에 의도한 대로 작동하지 않았다. 사소한 실수지만 React에서 요소를 렌더링 할 때 <body>를 그대로 쓰지 않고 <div id='root'></div> 를 사용하는 이유가 렌더링 공간을 따로 분리하는 의도였구나 생각한다.VanillaJs를 하면서 오히려 React의 편리함 측면에서 교훈을 많이 얻게 된다. 특히 DOM을 일일이 변경시키는 작업을 통해 React에서 상태가 변화하면 UI가 자동으로 바뀐다는 게 얼마나 경이롭고 편리한 기능인지 느꼈다.미션 1과는 달리 이번에는 모든 요소를 js에서 생성해보았는데, html 코드로 골격을 어느정도 짜 놓은 상태에서 로직을 붙이듯 작성하는 것이 html / css / js 가 기능적으로 분리되어 가독성 측면에서 적절하다 생각한다. 

프론트엔드인프런워밍업클럽

xicodey

[인프런 워밍업 클럽 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<FruitTotalPriceResponse> 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기백엔드

[인프런 워밍업 클럽 스터디] BE 스터디 1기 1주차 발자국

강의 수강일주일 동안 학습했던 내용을 요약해주세요.일주일 간의 학습 내용에 대한 간단한 회고를 작성해 주세요.섹션 0.Java, inteliiJ, Postman, MySQL, git 설치 방법에 대해 배웠습니다.Cmd를 통하여 Mysql에 접속하는 방법으로 데이터베이스, 테이블 조작을 할 수 있다는 것을 알게 되었습니다.MySQL CMD 접속 방법mysql -u [계정] -p [데이터베이스]저는 root 계정과 비밀번호를 설정하여mysql -u root -p 타이핑 후비밀번호 타이핑CMD에서 MySQL을 찾지 못하는 경우에는환경변수에 시스템 변수에 Mysql폴더의 bin폴더까지 주소를 복사하여 넣어주어야합니다. 섹션 0 회고다시 Java, inteliiJ, Postman, MySQL, git 설치 방법에 대해 보게 되니까 예전에 해봤던 기억이 떠오르고 미쳐 몰랐던 inteliiJ에 대해 알아가는 시간이 되었고 Postman을 저번 프로젝트를 할때는 자주 사용하지 않았는데 간편하게 사용할 수 있는 테스트 도구라서 자주 사용할 거 같습니다.  섹션 1.컴파일 : 인간이 이해하기 쉬운 언어를 기계어로 번역하는 과정컴파일러 : 컴파일을 하는 프로그램바이트 코드 : 0 , 1로 이루어진 코드, 컴퓨터가 이해하는 기계어java는 JVM을 통하여 기계어로 OS에 맞게 번역해준다.JVM(Java Virtual Machine) 자바 가상 머신의 약자OS 별로 존재바이너리 코드를 읽고 검증하고 실행 JRE(Java Runtime Environment)JVM + 자바 프로그램 실행에 필요한 라이브러리 파일 등JVM의 실행환경을 구현JDK(Java Development Kit)JRE + 개발을 위한 도구컴파일러, 디버그 도구 등이 포함 섹션 1 회고자바의 작동원리와 SpringBoot에 대한 기본적인 작동 흐름과 CRUD에서 CR에 대해 배우게 되었고, 잘 읽어야 다른 동작에 대해 쉽게 접근할 수 있을거 같아 배운것을 돌아보고 원리에 대해 되집어 보는 시간이 되었습니다. 섹션 2.Mysql에서 테이블만들고, 데이터를 조작하는 방법을 배움sql문을 통하여 intelliJ에서 Update, Delete에 관한 API를 작성섹션 2 회고inteliiJ와 Mysql을 연결하는 작업을 하였습니다. 연결하고 올바르게 데이터가 들어가는게 중요하다 생각이 들었고 sql문이 잘못 작성되어 곤란한 경우가 있었습니다. 찾는 것도 힘들었지만 sql문에 대해 좀 더 정확하게 작성하여 올바르게 전송되게끔 꼼꼼히 작성해보겠습니다.섹션 3. 클린코드에 대해 배우고 기존에 있던 코드를 리펙토링하며 더 분리시키고 깔끔한 코드로 작성하는 방법을 배웠습니다.  섹션 3 회고클린코드에 중요성과 작성하고 있는 코드에서 더 좋은 코드가 될 수 있을 지에 대한 고민이 많이 생긱나는 강의였고, 개발에는 끝없는 고민으로 시작되는거 같습니다.미션미션을 해결하는 과정을 요약해 주세요.미션 1 : https://www.notion.so/1-9ebaa9acab404522894cadf7aada5eb0 -> 구글 검색을 통해서 어노테이션에 대한 미션에 대한 정보들에 대해 보고 실습해보았습니다.미션 2 : https://www.notion.so/2-8e30c53ecab443e5bb3d3a1bcf8abcb4-> 강의를 보고 원하는 Get, Post라던지 관한 정보를 입력하고 어떻게 출력되는지 건드려보게 되었습니다.미션 3 : https://www.notion.so/3-7516e809e6134f0fbc255c3f57d239bb-> 검색을 통하여 람다식에 대해 알게 되었고 잘 활용만 한다면 코드를 단축시키고 클린 코드를 만들어 보게끔 연습을 해보아야겠다는 생각이 들었습니다. 

백엔드백엔드인프런스터디

winnercold

인프런 워밍업 클럽 BE 1기 - 1주차 발자국

학습한 내용Java컴파일: 인간이 이해하기 쉬운 언어를 기계어(바이트 코드)로 번역하는 과정컴파일러: 컴파일을 하는 프로그램바이트 코드: 0과 1로 이루어진 코드, 컴퓨터가 이해할 수 있다.자바의 JVM은 다양한 운영체제에서 바이트 코드를 실행할 수 있도록 중간 단계 역할을 한다.따라서 JVM만 운영체제별로 존재한다면, 하나의 바이트 코드로 각각의 운영체제에서 실행 할 수 있다.JVM자바 바이트 코드를 실행하는 가상머신바이트 코드를 읽고 검증 및 실행한다.Java 외에 다른 언어에서도 사용된다. JRE자바 애플리케이션 실행 환경JVM의 실행환경 구현 JDK컴파일러, 디버그 등을 포함하는 자바 개발 도구 Build소스 코드 파일을 컴퓨터에서 실행할 수 있는 독립 SW 가공물로 변환시키는 과정 빌드 과정소스 코드를 컴파일한다.테스트 코드를 컴파일 한다.테스트 코드를 실행한다.테스트 코드 리포트를 작성한다.기타 추가 설정한 작업들을 진행한다.패키징을 수행한다.최종 SW 결과물(Artifact)을 만들어 낸다.실행작성한 코드(혹은 테스트 코드)를 컴파일 후 실행해 보는 과정독립 SW 가공물이 생성될 수도 있고, 생성되지 않을 수도 있다.인터프리터 언어의 경우, 컴파일 과정이 필요 없다.빌드 툴(Build Tool)빌드 과정을 자동으로 처리 해주는 프로그램외부 소스 코드(외부 라이브러리) 자동 추가, 관리빌드란 단순히 실행하는 것과 다르다.빌드 과정 자동화와 외부 라이브러리 관리를 위해 빌드 툴이 사용된다.네트워크(Network)여러 컴퓨터나 장치들이 서로 데이터를 주고받을 수 있도록 연결된 시스템IP: 네트워크 상에서 각 장치를 구별하기 위해 사용되는 고유의 식별 번호도메인 이름: 외우기 어려운 IP 주소 대신, 사람이 읽을 수 있는 이름으로 대체할 수 있는 별칭포트: 하나의 IP 주소 내에서 특정 프로그램이나 서비스를 식별하기 위해 사용되는 숫자HTTP(Hypertext Transfer Protocol)인터넷에서 데이터를 주고받기 위한 표준 프로토콜프로토콜(Protocol): 통신을 위한 규칙과 약속규칙HTTP Method: 요청을 받는 컴퓨터에게 요구하는 행위GET: 데이터 요청(쿼리 사용)POST: 데이터 전송(바디 사용)PUT: 데이터 수정(바디 사용)DELETE: 데이터 삭제(쿼리 사용)Host: 요청을 받는 컴퓨터의 정보를Path: 요청하는 자원의 경로 데이터 전달 방법쿼리(Query) URL에 포함되어 데이터를 전달?를 통해 쿼리와 Path를 구분한다.쿼리 사이에는 &를 통해 구분한다.본문(Body) 별도의 본문을 통해 데이터를 전달API(Application Programming Interface)클라이언트와 서버 간의 HTTP 통신을 통해 정해진 약속에 따라 특정 기능을 수행인터페이스(Interface): 규약, 규칙JSON(JavaScript Object Notation)객체를 표현하기 위한 형식(객체 표기법)용법중괄호(`{}`)로 묶여 있다.중괄호 안에, "key": value로 표기한다.키("key")와 값("value")은 :로 구분된다.속성(키-값)은 ,로 구분한다.데이터베이스(Database)데이터를 구조화 시켜 저장하고, 관리하는 시스템RDB(Relational Database)는 데이터를 표(table) 형식으로 구조화하여 관리한다. SQL(Structured Query Language)표 형식으로 구조화된 데이터를 조회, 삽입, 수정, 삭제하는 언어SQL을 통해 데이터베이스와 상호작용DDL(Data Definition Language)데이터를 정의하기 위한 SQL데이터베이스 생성CREATE DATABASE [데이터베이스 이름];데이터베이스 목록 조회SHOW DATABASES;데이터베이스 삭제DROP DATABASE [데이터베이스 이름];데이터베이스 선택USE [데이터베이스 이름];테이블 생성 CREATE TABLE [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드2 이름] [타입] [부가조건], ... PRIMARY KEY ([필드 이름]) );테이블 목록 조회SHOW TABLES;테이블 삭제DROP TABLE [테이블 이름];DML(Data Manipulation Language)데이터 조작을 위한 SQL데이터 삽입 (생성 - Create)INSERT INTO [테이블 이름] (필드1이름, 필드2이름, ...) VALUES (값1, 값2, ...)데이터 조회 (읽기 - Retrieve or Read)SELECT * FROM [테이블 이름] WHERE [조건];데이터 수정 (업데이트 - Update)UPDATE [테이블 이름] SET 필드1이름=값1, 필드2이름=값2, ... WHERE [조건];데이터 삭제 (제거 - Delete)DELETE FROM [테이블 이름] WHERE [조건];수정과 삭제 시, 조건을 지정해주지 않는다면 모든 데이터에 적용되기 때문에 주의해야 한다.클린 코드(Clean Code)코드(Code)는 요구사항을 표현하는 언어다.개발자는 이러한 요구사항을 구현하기 위해 코드를 읽고 작성한다.코드를 읽는 것은 소프트웨어 개발에서 필수적이며 피할 수 없다.작성 원칙함수는 작고 명확하게 작성되어야 한다.클래스는 하나의 책임만을 가져야 한다.이유협업 과정에서 다수의 개발자가 동시에 수정할 수 없다.코드를 읽고, 이해하기 어렵다.함수의 일부분을 수정하더라도, 수정된 코드가 다른 부분에 영향을 미칠 수 있기 때문에 함부로 수정할 수 없게 된다.코드의 기능이 커서 테스트가 어렵다.이로인해 유지보수성이 떨어진다.회고지금까지는 Java의 다양한 기능을 사용하는 과정에서 그저 기능들을 활용하는 데 그쳤던 것 같다. 그러나 이번 스터디를 통해 강의를 듣고 미션을 수행하면서 해당 기능들이 등장한 배경과 목적을 공부할 수 있었고, 앞으로 어떻게 공부해야 하는지 배울 수 있었던 1주차 였다.

백엔드