블로그

이용수

[인프런 워밍업 클럽 1기] BE 2일차 과제

[인프런 워밍업 클럽 1기] BE 2일차 과제문제 1구현 과정구현 목표 : 두 수를 입력해 요청하면 덧셈, 뺄셈, 곱셈의 결과를 반환하여 응답함.메서드 타입 : GET경로 : /api/v1/calc쿼리 파라미터 이름 : num1 / 타입 : int이름 : num2 / 타입 : intController 구현 - CalcController.javapackage com.group.libraryapp.controller.assignment.calc; import com.group.libraryapp.dto.assignment.request.CalculatorCalcRequest; import com.group.libraryapp.dto.assignment.response.CalculatorCalcResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class CalcController { @GetMapping("/api/v1/calc") public CalculatorCalcResponse calcTwoNumbers(CalculatorCalcRequest request ) { return new CalculatorCalcResponse(request); } }Request DTO 구현 - CalculatorCalcRequest.javapackage com.group.libraryapp.dto.assignment.request; public class CalculatorCalcRequest { private int num1; private int num2; public CalculatorCalcRequest(int num1, int num2) { this.num1 = num1; this.num2 = num2; } public int getNum1() { return num1; } public int getNum2() { return num2; } }Response DTO 구현 - CalculatorCalcResponsepackage com.group.libraryapp.dto.assignment.response; import com.group.libraryapp.dto.assignment.request.CalculatorCalcRequest; public class CalculatorCalcResponse { private int add; private int minus; private int multiply; public CalculatorCalcResponse(CalculatorCalcRequest num) { this.add = num.getNum1() + num.getNum2(); this.minus = num.getNum1() - num.getNum2(); this.multiply = num.getNum1() * num.getNum2(); } public int getAdd() { return add; } public int getMinus() { return minus; } public int getMultiply() { return multiply; } } 서버 실행 후 Postman 결과 확인문제 2구현 과정구현 목표 : 날짜를 입력해 요청하면 해당 날짜 요일을 반환하여 응답함.메서드 타입 : GET경로 : /api/v1/day-of-the-week쿼리 파라미터 이름 : date / 타입 : LocalDate Controller 구현 - dayController.javapackage com.group.libraryapp.controller.assignment.day; import com.group.libraryapp.dto.assignment.response.DayOfTheWeekResponse; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDate; @RestController public class dayController { @GetMapping("/api/v1/day-of-the-week") public DayOfTheWeekResponse getDayOfWeek(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) { return new DayOfTheWeekResponse(date); } }Request DTO 구현 - DayOfTheWeekRequest.javapackage com.group.libraryapp.dto.assignment.request; import java.time.LocalDate; public class DayOfTheWeekRequest { private LocalDate date; public DayOfTheWeekRequest(LocalDate date) { this.date = date; } public LocalDate getDate() { return date; } }Response DTO 구현 - DayOfTheWeekResponse.javapackage com.group.libraryapp.dto.assignment.response; import java.time.LocalDate; public class DayOfTheWeekResponse { private String dayOfTheWeek; public DayOfTheWeekResponse(LocalDate date) { this.dayOfTheWeek = date.getDayOfWeek().toString(); } public String getDayOfTheWeek() { return dayOfTheWeek.substring(0, 3); } }서버 실행 후 Postman 결과 확인문제 3구현 과정구현 목표 : 여러 수가 입력된 배열을 입력해 요청하면 배열 내 수의 총합을 반환하여 응답함.메서드 타입 : POST경로 : /totalsum쿼리 파라미터 이름 : numbers / 타입 : ListController 구현 - TotalSumController.javapackage com.group.libraryapp.controller.assignment.totalsum; import com.group.libraryapp.dto.assignment.request.TotalSumRequest; import com.group.libraryapp.dto.assignment.response.TotalSumResponse; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class TotalSumController { @PostMapping("/totalsum") public TotalSumResponse getTotalSum(@RequestBody TotalSumRequest request) { return new TotalSumResponse(request); } }Request DTO 구현 - TotalSumRequest.javapackage com.group.libraryapp.dto.assignment.request; import java.util.List; public class TotalSumRequest { private List<Integer> numbers; public List<Integer> getNumbers() { return numbers; } }Response DTO 구현 - TotalSumResponse.javapackage com.group.libraryapp.dto.assignment.response; import com.group.libraryapp.dto.assignment.request.TotalSumRequest; public class TotalSumResponse { private int sum; public int getSum() { return sum; } public TotalSumResponse(TotalSumRequest request) { this.sum = request.getNumbers().stream().mapToInt(Integer::intValue).sum(); } }서버 실행 후 Postman 결과 확인 

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

이용수

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

[인프런 워밍업 클럽 1기] BE 1일차 과제 [질문]Q. 어노테이션을 사용하는 이유 (효과) 는 무엇일까?  A. 어노테이션을 사용하는 이유에 대해 아래 내용에서 어노테이션의 정의, 특징, 역할, 장,단점에 대해 자료를 찾아 정리해보면서 나름대로 다음과 같은 결과를 도출 했다.어노테이션을 사용하면 코드와 설정을 같은 위치에 배치하여 코드의 가독성을 향상시킨다.클래스, 메서드, 필드, 파라미터 등과 관련된 정보가 함께 있어 코드를 읽고 이해하기 쉬워지며 특히, 코드의 흐름을 파악하기 쉬워진다.별도의 설정 파일을 작성하지 않고도 어노테이션을 사용하여 설정을 간소화할 수 있다. 이는 개발자가 코드에 직접 설정을 기술할 수 있으므로 설정 관리를 단순화시킨다.어노테이션을 통해 공통적인 코드 패턴이나 설정을 재사용할 수 있다. 이는 코드의 중복을 줄이고 효율적으로 코드를 작성할 수 있도록 도와준다.필요한 기능이나 제약 사항을 정의하기 위해 커스텀 어노테이션을 직접 정의할 수 있다. 이것은 프로젝트에서 특정한 요구사항에 대응하기 위해 유연하고 효율적인 방식으로 사용할 수 있다.어노테이션 프로세서를 사용하면 컴파일 시점에 어노테이션을 처리하고 검증할 수 있다. 또한, 코드를 자동으로 생성하거나 수정할 수 있어서 프로젝트에 필요한 기능을 효과적으로 구현할 수 있다. 어노테이션의 정의자바에서 어노테이션(Annotation)이란 소스 코드에 메타데이터를 추가하는 방법을 제공하는 기능이다. 어노테이션의 특징어노테이션은 컴파일러나 런타임 환경에게 정보를 전달할 수 있으며 전달된 정보는 코드를 실행하거나 컴파일할 때 사용된다.어노테이션은 @ 기호로 시작하며, 주석과 유사하게 생겼지만, 주석과는 달리 컴파일러가 읽고 처리할 수 있다.정의된 어노테이션은 해당 타겟에 대한 동작을 수행하는 프로그램 외에는 다른 프로그램에게 영향을 주지 않는다.        어노테이션의 역할컴파일러에게 문법 에러를 체크하도록 정보를 제공한다.프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.런타임에 특정 기능을 실행하도록 정보를 제공한다.  어노테이션 사용의 장점코드의 가독성 향상어노테이션은 코드와 설정을 같은 위치에 배치하므로 읽고 이해하기 쉽다. 클래스, 메서드, 필드, 파라미터 등 연관된 코드와 가까이 있기 때문에 흐름을 따라가기 쉽다.설정의 간소화별도의 설정 파일 작성 없이 어노테이션 적용을 통해 설정을 간소화할 수 있다.중복 코드 제거공통적인 코드 패턴이나 설정을 재사용할 수 있기 때문에 코드의 중복을 줄이고 효율적으로 코드를 작성할 수 있다.커스텀 어노테이션 정의직접 커스텀 어노테이션을 정의함으로 필요한 기능이나 제약 사항을 정의하여 사용할 수 있다.프로세서를 통한 검증 및 코드 생성어노테이션 프로세서를 이용해 컴파일 시점에 어노테이션을 처리하고 검증할 수 있다. 또한 코드를 자동으로 생성하거나 수정할 수 있기에 효과적으로 기능을 구현할 수 있다. 어노테이션 사용의 단점런타임 오버헤드런타임 시점에 리플렉션을 사용하여 처리하는 어노테이션의 경우 성능상의 오버헤드가 발생할 수 있다.컴파일 시점 제한어노테이션도 컴파일 시점에 오류를 확인할 수 있지만, 어노테이션 로직이 런타임에 에러를 발생시키거나 어노테이션에 잘못된 값이 할당된 경우 컴파일 시점에 오류를 확인할 수 없을 수도 있다. 어노테이션의 종류어노테이션은 크게 세 가지로 구분된다. 자바에서 기본적으로 제공하는 빌트인 어노테이션과 어노테이션을 정의하는 데 사용되는 메타 어노테이션, 마지막으로 사용자 어노테이션이 있다. 빌트인 어노테이션자바에서 기본적으로 제공하는 어노테이션이다.@Override : 컴파일러에게 메서드를 오버라이딩하는 것이라고 알린다.@Deprecated : 앞으로 사용하지 않을 대상임을 알린다.@FunctionalInterface : 함수형 인터페이스라는 것을 알린다.@SuppressWarning : 컴파일러가 경고 메시지를 나타내지 않는다.@SafeVaragrs : 제네릭과 같은 가변 인자의 매개변수를 사용할 때의 경고를 나타내지 않는다. 메타 어노테이션어노테이션에 붙이는 어노테이션으로, 어노테이션을 정의하는 데 사용한다.@Target : 어노테이션을 정의할 때 적용 대상을 지정하는 데 사용한다.@Documented : 어노테이션 정보를 javadoc으로 작성된 문서에 포함시킨다.@Inherited : 어노테이션이 하위 클래스에 상속되도록 한다.@Retention : 어노테이션이 유지되는 기간을 정하기 위해 사용한다.@Repeatable : 어노테이션을 반복해서 적용할 수 있도록 한다. 사용자 정의 어노테이션사용자가 직접 정의하여 사용하는 어노테이션이다. Q. 나만의 어노테이션은 어떻게 만들 수 있을까? A. 사용자가 직접 여러 어노테이션을 혼합하거나 정의하여 어노테이션을 만들 수 있다.기본적으로 인터페이스를 정의하는 것과 유사하며 @interface 뒤에 사용할 어노테이션의 이름을 정의하고 속성을 설정한다.어노테이션을 정의할 때 기본적으로 포함되야할 메타 어노테이션이 존재한다.@Retention - 어노테이션이 유지되는 기간을 정해야 하며 다음과 같은 열거 상수 중 선택한다.SOURCE : 컴파일할 때 적용 ~ 컴파일된 후에 제거됨CLASS : 메모리로 로딩할 때 적용 ~ 메모리로 로딩된 후에 제거됨RUNTIME : 실행할 때 적용 ~ 계속 유지됨@Target - 어노테이션을 정의할 때 적용 대상을 지정해야 하며 다음과 같은 열거 상수 중 선택한다.TYPE : 클래스, 인터페이스 열거타입ANOTATION_TYPE : 어노테이션FIELD : 필드CONSTERUCTOR : 생성자METHOD : 메서드LOCAL_VARIABLE : 로컬 변수PACKAGE : 패키지 이제 예를 들어 @MyAnnotion이라는 String 속성을 가진 어노테이션을 정의해보겠다.import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 사용자 정의 어노테이션 선언 @Retention(RetentionPolicy.RUNTIME) // 어노테이션 정보를 유지할 시점 (런타임까지 유지) @Target(ElementType.METHOD) // 어노테이션이 적용될 대상 (메서드에 적용) public @interface MyAnnotation { String value() default "test"; // 어노테이션의 속성, 기본값은 빈 문자열 // 추가적인 속성이 필요하다면 여기에 선언할 수 있습니다. }정의한 어노테이션에 사용한 메타 어노테이션은 다음과 같다.@Retention(RetentionPolicy.RUNTIME): 어노테이션 정보를 런타임까지 유지한다는 것을 의미하며 이렇게 하면 실행 중에 리플렉션(reflection)을 사용하여 어노테이션 정보를 읽을 수 있다.@Target(ElementType.METHOD): 이 어노테이션은 메서드에만 적용하도록 설정한다. 이제 메인 코드에서 어노테이션을 사용하고 리플렉션을 통해 어노테이션의 정보를 출력해보자.public class Main { @MyAnnotation public void myMethod() { System.out.println("This is myMethod"); } public static void main(String[] args) throws Exception { Main example = new Main(); example.myMethod(); // 리플렉션을 이용하여 어노테이션 정보 출력 java.lang.reflect.Method method = Main.class.getMethod("myMethod"); MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); System.out.println("Method name: " + method.getName()); System.out.println("Annotation value: " + annotation.value()); } }어노테이션의 이름과 속성 값을 출력하는걸 확인할 수 있다."C:\Program Files\Java\jdk-11.0.16.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.1.1\lib\idea_rt.jar=6433:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.1.1\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\User\Desktop\test\out\production\test Main This is myMethod Method name: myMethod Annotation value: test Process finished with exit code 0참고 자료[Spring] 스프링을 어노테이션 기반으로 만든 이유 (tistory.com)[Java] Custom Annotation(커스텀 어노테이션) 만들기 - MangKyu's Diary (tistory.com) 

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

한국 IT 용어 이야기 (3) - "R&R"

여러 직군의 사람들과 일을 하게 되었을 때, 애자일/스프린트/회고/리뷰 등으로 이런 저런 일들을 진행하게 될 때 듣게 되었던 단어 중에 생소한 것으로 "R&R"이 있었다. 스쳐 지나듯 오갔던 말이어서 한글로 "알앤아ㄹ" 혹은 "아래날" 정도로 들려서 꽤 오랫동안 'arena' 를 이야기하는 줄 알고 있었다. 'arena' 는 발음으로는 '어리나'에 가깝지만, 한국에서는 '아레나' 라고 듣고 자라 왔었기에 한데 모여 전투적으로 열심히 일하자는 이야기겠구나 생각했었는데, 사실은 정반대의 의미를 가진 단어였던 셈이었다.참고로 구글, 바드, 웹스터 등에 R&R 을 물어보면 Rest & Recreation 을 알려 주는데https://www.merriam-webster.com/dictionary/R%20%26%20R여기에 한글로 '뜻'이라 물어 한국어를 섞어 주면 IT 용어들로 쏠려서 결과들이 몰려 온다. 관련업에 종사하긴 하지만, 한국어 컨텐츠가 쏠려 있는 거 같아 조금 씁슬해 진 부분도 있겠다.https://www.google.com/search?q=R%26R+%EB%9C%BB알앤알 뒤에 붙는 단어들로는 '정리하다', '구분하다'가 많이 오고, 비슷한 문맥에 '업무분장'이라는 이름의 단어도 종종 등장한다. 업무분장은 job assignment , task assignment 등에 더 가깝다 하겠다.몇몇 기억들주로 여럿이 모여 일을 같이 하면서 혹은 나누어 하게 될 때 '선을 긋는' 용도로 자주 쓰였던 기억이다. 팀간에 혹은 멤버들 간에 가벼운 텐션이 있게 될 경우 나는 여기까지만 할 거고 그쪽에서 나머지는 알아서 하라 정도의 거리 두기 용으로 ...프로젝트 단위 보다는 조직도 같이 큰 그림에서 이해하기에 괜찮은 개념들이긴 하지만, 뭔가 훨씬 더 규모가 큰 곳들 - 영업망 업권 할당 같은 - 에서 쓰이는 게 좋은 개념이 과하게 스타트업씬에 내려와 있는 게 아닐까 싶었다. 특히 'responsibility' 부분은 과제에 적용시키기 어렵다는 생각인데, 실제로 '책임을 진다'는 게 어떤 의미인가에 대해 딱 떨어지는 그림이 나오진 않았고, 이는 내가 각종 툴의 "assigned" 상태에 익숙해져 있는 bias 가 있다 하겠다.아이러니하게 구글에 다니면서는 한 번도 써 보지 않은 단어였고, 그래서인지 개인적으로 개발 조직 내에서는 적어도 선을 그으면 안 된다는 생각이다. 잘 풀릴때는 뭐 별 문제 없지만, 왠지 과제가 삐걱거릴 때 무의식적으로 선을 긋는 습관이 여기서 온 거 같다는 생각이고, 몇몇 경우 개인들과 조직의 성장을 막는 요소로 작용하고 있지 않았나 하는 생각이다. 예를 들면 '나는 프론트엔드 엔지니어니까 백앤드는 고치면 안돼' 같은..선을 긋고 기본 자세가 방어적인 데서 시작을 하는 팀들과 복잡한 일을 해 나갈 때, 자연스레 비는 부분에 대해 책임 소재가 불분명해져서 종종 어려운 일들이 생겼다. 사람 수가 일감의 수보다 부족한 거의 모든 스타트업 씬에서는 특히 자주 일어나는 일인데, 새로운 영역의 일이거나 몇몇 팀들의 사이에서 겹치거나 비거나 하는 경우 자발적으로 알아서 챙겨 지면 좋으련만.. 이걸 잘 나누어서 일이 되게 잘 시키는(?) 것도 PM 이나 리더십의 R&R 이라 생각할 수도 있겠다.

교양용어개발자한국어

한국 IT 용어 이야기 (2) - 정합성

두번째로 챌린징했던 단어는 '정합성'이다. PM / design 쪽에서 이야기는 많이 듣지 못했지만, data 직군과 DBA , DevOps 들과 이야기할 때 종종 나왔던 단어이다.일단 네이버 사전에서 정합성은 무슨 말인지 못 알아들을 정도의 설명인데, 단지 뒤에 '체크'라는 말이 붙으면서 조금 알아들을 수 있는 용어로 바뀌게 된다.네이버 사전 결과 '정합성'30년 전에 데이터베이스 과목을 수강한 후에 실무 일머리들은 영어로 다시 다 배웠기에 여러 가지 용어들을 두리뭉실하게 써 왔는데, 여기서 잠깐 ChatGPT 와 bard 의 이야기 먼저...ChatGPT 의 결과 - "데이터 정합성을 영어로"bard 의 결과 - "데이터 정합성을 영어로"일단 책에서 배운 개념으로 data consistency 와 data integrity 가 꼬이기 시작했고, 한글로 적당한 '데이터 무결성'이 생각이 났다. 이를 비교하려 다시 물어 보니 이제 bard 랑 chatGPT 가 비슷한 말을 하게 되는 거 같았다.ChatGPT - 데이터 무결성과 데이터 정합성 비교bard - 데이터 무결성과 데이터 정합성 비교아래는 배웠던 대로 이해하고 동작하는 (쉬운) 예제들.database migration 작업을 하는데, 새로 생성된 테이블의 entry 개수가 이전 table 의 개수와 다르다.. --> 두 테이블의 정합성이 맞지 않아 AWS DMS 를 다시 시도한다든지...database , table 안에 끊어진 reference 들이 있고, deprecated 된 table 때문에 의미 없는 필드들이 더 생기게 되었다. --> data 무결성이 깨지는 상황으로 batch 잡을 돌려서 null 로 채우자.. 조금 난이도가 있는 사례로는소스로 삼는 raw table 이 여러 곳에서 동시에 사용되는 derived table 을 만들게 되는데, 같은 날 생성된 다른 두 테이블의 같아야 할 값이 다르더라. --> 두 테이블 사이에 필드들이 정합성이 다르다. freezing 되어 있는 테이블을 써라.. 그런데, 위의 bard 의 번역처럼 다양한 의미를 두리뭉실하게 '정합성'이라는 말에 기대어 쓰는 경우들이 종종 있었다. 뭔가 딱히 깊이 설명하고 싶지 않지만, 보이는 데이터를 바로 쓰기 찜찜할 때 '정합성' 이 거론되었고, 사실 이 단어 뒤에 들어오게 될 동사를 고르는 것도 꽤 어려운 일이다. '맞지 않다' , '깨져 있다', '좋다 or 나쁘다'. '완벽하다', '쓸만하다?'가장 어려웠던 사례로는Google Analytics 가 주는 MAU, Firebase 가 주는 MAU , Amplitude 가 주는 MAU 가 다른데, 데이터 정합성이 의심되니 쓰던 걸 쓰도록 하겠다. or vice versa실험을 돌려 지표가 나왔는데, 정합성에 이슈가 있어서 다시 하기로 했다. 이 '정합성'이라는 말은 '무결성'에 비해 조금 과하게 넓게 쓰이고 있는 게 아닐까 하는 생각이었고, 이 일본식 한자들은 딱히 정이 가지 않기도 해서 어느 새 지나 보니 시간 될 때마다 영어 표기를 권하는 꼰대가 되어 있었다.

교양용어개발자한국어

mikro-orm 버그 리포팅 후기 (feat. auto increment)

이슈등록 링크: https://github.com/mikro-orm/mikro-orm/issues/5460 [ 문제 상황 ] 사내에서 mikro-orm 을 Mysql 과 사용하고 있는데, 사내 db는  짝수 채번을 하고 있는데, 이상하게 mikro-orm 에서 여러 entity 를 영속화하면 pk 가 순차적으로 나오는 문제가 있었다.  [ 이슈의 정체 ] JPA의 경우 ORM 채번을 할 때, 아래의 mysql ok packet 을 통해서 이루어진다. https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html 이후에 auto_increment 에 대한 설정 값을 요청하여서, 이를 기반으로 영속성 컨텍스트에 auto_increment pk 를 세팅을 하기 마련이다. 단순 repository.save 호출을 한다고 가정하면, 아래의 패킷이 날아간다. 1. begin 2. insert into 'table' ('데이터')3. commit 그리고 2번 요청의 응답 패킷(OK packet)에 자세히 보면 아래와 같이 Last INSERT ID 라는 값이 내려온다. 이 값을 기준으로 entity 에 id 를 세팅해준다. (참고로, commit 되기전에 db 에서는 채번이 되고, db 에서 한번 채번되는 경우에 tx 가 롤백이 되더라도 이후에 같은 값을 사용하지는 않는다 -> pk 는 항상 순차가 아닐 수도 있음) 사실 이런 부분은 mikro-orm 도 똑같을거라고 생각을 했는데, mikro-orm 자체가 어떤 특별한 재주를 부리는건 아닐거라서 MySQL 서버와의 통신을 통해서 채번한 결과를 사용하는건 당연해보였다.  그런데, 아래와 같이 여러 개의 entity 를 저장하는 경우에 이상하게 pk 가 순차로 나왔다. (실제 코드는 아니고, 단순화하였다)mysql 채번 결과를 바탕으로 entity 의 pk가 정해질 텐데,  pk가 순차적으로 생성되니 의아했다.const result = await this.postRepository.save([ new PostEntity('title'), new PostEntity('title2'), new PostEntity('title3'), ]); return result; } @Injectable() export class PostRepository { constructor( @InjectRepository(PostEntity) private readonly postRepository: EntityRepository<PostEntity>, ) {} async save(entity: PostEntity | PostEntity[]): Promise<PostEntity | PostEntity[]> { await this.postRepository.getEntityManager().persistAndFlush(entity); return entity; } }  그래서, 해당 패킷이 어떻게 나가는지 wireshark 를 통해 패킷 분석을 해보았다 wireshark 를 사용하여 packet 을 확인해보니 auto_increment 에 대한 쿼리가 패킷으로 잘 날아갔다.  Ok packet이 잘 왔고, auto_increment 변수에 대한 값도 잘 조회하고 있었다.  그런데 결과는 아래와 같았다. - entity: pk 는 1씩 증가- tx commit 이후 db: db 레코드의 pk는 2씩 증가하고 있었다. mikro-orm 버그가 확실해 보였고, 브레이크 포인트를 찍어서 확인을 해보았다.  아래 코드에서 this.autoIncrementIncrement 필드에 1을 세팅하고 있는게 문제의 원인이었다.mikro-orm 의 connection 은 mikro-orm/knex 라는 라이브러리를 통해 이루어지는데, 여기서 res는  Value 로 내려오는데,할당 시에는 res?.auto_increment_increment 로 할당하고 있던게 문제였다.async getAutoIncrementIncrement(ctx) { if (this.autoIncrementIncrement == null) { // the increment step may differ when running a cluster, see https://github.com/mikro-orm/mikro-orm/issues/3828 const res = await this.connection.execute(`show variables like 'auto_increment_increment'`, [], 'get', ctx, { enabled: false }); /* istanbul ignore next */ this.autoIncrementIncrement = res?.auto_increment_increment ? +res?.auto_increment_increment : 1; } return this.autoIncrementIncrement; }  [ 후기 ] 사실 팀 내에서 이 문제에 대해서 인지는 하고 있었고, 다른 방식으로 문제를 해결하고 있었다. 그런데 시간이 나서, 트러블 슈팅을 해보았고 위와 같은 이슈가 있다는 것을 알게 되었던 것이다.  그래서 이 부분에 대해서 이슈를 등록했고, 올라온지 몇시간도 안되어서 수정되었다. 사실 처음부터 pr 을 올릴까 말까 고민을 했는데, orm 특성상 driver 의 버전에 의해 생기는 버그일 수도 있다고 생각을 해서 pr 을 올리지 않고 상세하게 이슈 리포팅을 했다. 무지성 pr을 올릴걸하는 아쉬움이 남는다 ㅋㅋtistory 게시글 링크: https://pius712.tistory.com/22

백엔드mikro-rommikroormnodejsnestjs

김종한

[찍먹클럽] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈 강의 후기 feat.인프

[찍먹클럽] C#과 유니티로 만드는 MMORPG 게임 개발 시리즈 강의 후기 feat.인프런 인프런_찍먹클럽 Unity 개발 교육이번 KG 카이로스 교육에서 Unity를 사용해 프로젝트의 질을 높이기 위해서 많은 강의를 찾던 중..."인프런" 에서 Unity 강의를 1달간 무료로 제공되는 [찍먹클럽] 이벤트를 모집했었습니다!아침 Unity를 입문에 관련 교육이 필요했었고 특히나 돈이 없는 취업 준비생 입장에서 무료 강의는정말 달콤한 소식이 아니었나 싶었습니다.찍먹클럽에 선정되었다!내가 정말 열심히 블로그를 운영해서 그런가?운이 좋게도 [찍먹클럽] 에 선정되었다![C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진출처 : 인프런https://bit.ly/3V7xZts이번에 무료로 들었던 강의는 위의 링크를 통해서 자세하게 확인할 수 있습니다!무엇보다도 인프런은 높은 질의 강의를 제공하는 플랫폼으로 잘 알고 있었습니다.특히나 개발자들도 많이 찾는 강의 플랫폼이어서 더욱 신뢰가 갔었습니다.거기다가... 강사님의 엄청난 이력까지... 열심히 들을 이유가 생겼습니다!우선 강의에 대해서 좋은 점을 말씀드린다면1) 부드러운 진행과 필요한 내용으로 구성된 효율적 강의2) Unity 입문하면서 느끼는 Assets, Hierarchy처럼 복잡한 내용들을 집어줌3) 모델링만 하는 것이 아는 C#에 대한 내용도 포함된 복합적인 강의4) 개발하기 좋은 환경 구성까지 잡아 줌 (*레이아웃 설정, 폴더 정리 등)출처 입력가장 좋은 점이라면 [몰입도] 있는 강의가 아니었나 싶었습니다.아직 강의를 전부다 듣지 못했기 때문에... 지금까지 강의를 보고 구현한 정도만 리뷰하겠습니다! <Notion을 통한 강의 내용 정리>강의에서 중요한 내용들은 Notion에 따로 정리 <C# 코드 구현고 동작은 Unity 씬에서 확인>Unity는 C# Script를 작성해 동작을 제어하거나 새로운 Assets들을 추가할 수도 있습니다.필요하고 주로 사용하는 코드들은 따로 정리해서 내가 개발하고 싶거나 Object를 동작할 때 자주 활용했습니다.이번 강의에 대해 좋은 점도 코드 리뷰도 같이 해주셔서 상당히 만족했습니다.Vector3에 대한 코드 리뷰와 동작 확인 <Prefab을 통해 나만의 Tank 만들어 보기>Assets에 Prefab을 만들어서 내가 원하는 Object들을 생성하고 저장합니다.각 Prefab은 Speed나 각도를 독립적으로 지정해 동작을 시킬 수 있습니다.허접해 보이지만 탱크다.나만의 Tank를 동작해 봄!나의 Unity 공부는 -ing출처 입력최근에 KG 카이로스에 프로젝트를 기획하고 팀원들과 의견을 나누는 과정에 있어서바쁜(*핑계 아님) 와중에 강의를 절반 정도 들었습니다!KG_KAIROS 최종 프로젝트 <안>그래서... 앞으로의 남은 강의를 전부 수강하게 된다면 어떤 Unity 환경 속에 내가 구성한 Object를동작하고 플레이할 수 있을지 기대가 됩니다!남은 강의... 그중 미니 RPG가 상당히 기대된다! 인프런 담당자님... 반드시 전부 수강하겠습니다. 믿어 주십쇼!미련한 수강생

그래픽 디자인인프런인프런강의후기게임개발게임개발강의인강후기강의후기UnityC#유니티엔진MMORPG

[인프런 찍먹클럽] 언리얼엔진5 스파르타 클래스 - 심화편 후기

언리얼 엔진5를 공부를 하다보면, 영어 강의들을 보게 된다. 수많은 영어 강의들한국어 강의를 찾던 중, 인프런의 언리얼엔진5 스파르타 클래스 - 심화편을 알게 되었다.심화편이라.. ㄷㄷ 벌써부터 궁금해지지 않나?! 강사님은 YAL 선생님!실전, 심화로 나누어 2개의 강의를 제작하셨다. 실전편은 무료이니 모든 분들이 체험할 수 있다. 언리얼엔진5 스파르타 클래스 - 심화편 : https://bit.ly/3T7YBbd 특히, 나는 애니메이션을 더욱 알고 싶어서 이번에 참가하게 되었다.언리얼 엔진의 애니메이션의 다양한 방식들을 알 수 있다. IK,FK를 보면 무슨 생각이 들까요? 피하고 싶어진다...😥하지만, 강의를 들으면서 애니메이션에 사용되는 용어들을 쉽게 알 수 있었다. IK: Inverse Kinematic 축을 활용해 bone을 움직임FK: 관절 인형 처럼 관절들을 조종해서 움직임을 구현출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅  리타게팅! 애니메이션을 다른 캐릭터에 복사를 하는 것이다. 당연히 그대로 사용하면 bone의 사용방식이 다를테니 망가진다. 동기화를 해주기 위해서 사용하게된다. 1. 먼저 리타겟팅을 위해 bone 체인을 설정해준다.출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅2. 리타겟팅 에셋에서 Source IK Rig와 Target Ik Rig를 비교하면서 오차들을 수정하며 동기화 시킨다.출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅 3. 리타겟한 결과를 볼 수 있다.https://youtu.be/b9l-7svKbP4출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅 이 방식들을 강의를 들으면서 따라하면 쉽게 제작이 가능하다.중요한 사실은 이 강의를 기반으로 자신의 애니메이션을 제작하는데 활용 할 수 있다. 물론, 강의를 따라해도 안되는 경우들이 생긴다. 나도 물론 생겨서 당황스러웠지만, 커뮤니티 게시판으로 해결을 하였다.언리얼 엔진5 스파르타 클래스 - 심화편 커뮤니티 게시판여기 게시판을 활용해서 선생님께 직접 질문을 하고 답변을 받을 수 있으니, 걱정 안해도 될 것이다. 후기나는 이번 찍먹클럽을 통해서 애니메이션에 대한 두려움이 사라지게 되었다. 배운 것들을 기반으로 나의 프로젝트에서 애니메이션을  활용할 수 있음을 확인하여 나에게 만족스러운 강의가 되었다. 이 강의를 자신이 들어도 괜찮을까 고민 하는 분들!난이도가 매우 쉽게 구성되어 있고 커리큘럼이 단계적으로 잘 구성되어 있다. 선생님과 함께 따라서 제작한다면 무리가 없을 것이다.언리얼 엔진5 에디터도 무려 한국어로 되어 있어, 영어가 어려워 접근하기 쉽지 않은 분들도 할 수 있는 쉬운 접근 중 하나가 될 것이다. 모두 스파르타 클래스 - 심화편을 듣고 언리얼 엔진5를 마스터 할 수 있도록 노력하자! 화이팅!

게임 프로그래밍인프런인프런강의후기게임개발게임개발강의인강후기강의후기게임개발자인프런강의

sql - section 1

관계형 DBOLTP (Online Transaction Processing): 트랜잭션을 하기위한 데이터베이스특징보류, 중간 상태가 없어서 데이터의 무결성을 유지할 수 있다.데이터 추가, 변경이 많다.쿼리 속도가 느리다.트랜잭션: 데이터베이스의 상태를 변화시키기 위해 수행되는 작업의 단위관련 DBMSoracle데이터 베이스 시장 1위높은 안정성과 유지보수 보장비싼 가격mysql오픈소스데이터 베이스 시장 2위postgreSQL오픈소스mysql보다 sql 표준을 잘 지원하며, 쿼리가 복잡해질 수록 성능이 더 잘 나옴mssql대규모 엔터프라이즈 수준의 시스템에 적합주로 윈도우 환경에 사용default isolation level이 read committed데이터를 읽을 때 공유잠금이 유지 -> WITH(NOLOCK)을 통해 공유잠금없이 데이터 조회OLAP(Online Analytical Processing): 데이터 웨어하우스를 이용해, 분석질의를 처리 목적으로 만들어진 데이터베이스쿼리 속도가 빠른 편데이터 웨어하우스: 분석가능한 정보의 중앙 라포지토리관련 DBMS빅쿼리구글 클라우드의 OLAP + data warehouse 사용컴퓨팅 레이어와 스토리지 레이어 분리각 레이어가 다른 레이어에 영향을 안 미침비관계형 DB (NOSQL)특징key - value 형식을 지원PK,FK JOIN을 지원하지 않음스키마에 대한 정의가 없음장점대용량 데이터 처리에 유리관계형 데이터베이스보다 읽기, 쓰기 속도가 빠름데이터 모델링에 유리분산처리에 유리데이터의 일관성을 보장하지 않아도 되고, join 연산이 필요없을 때 사용하면 유리관련 DBMS몽고DBRedis  

데이터베이스

코딩웍스(Coding Works)

[Tailwind CSS] CLI설치 VS Code에서 npm 에러 환경 변수 설정

[Tailwind CSS로 개발자가 만드는 멋진 UI 스타일링] 강의 중 [섹션 3. Tailwind CSS CLI 환경 구축]에서 [Tailwind CSS에서 CLI 환경 구축(ft. Node.js)] 영상 참고자료입니다.비주얼스튜디오코드 커멘트 창에서 npm init을 실행하면 'npm'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. 라는 오류 메세지가 나올 수 있습니다. 이건 비주얼스튜디오코드에서 발생하는 오류인데 npm의 환경 변수를 설정해주면 해결되니 오류가 나면 아래처럼 해주시면 됩니다.1) 윈도우 검색창 또는 제어판에서 환경 변수를 검색하고 시스템 환경 변수 편집 클릭※ [환경변수]라고 붙여서 검색하지 마시고 [환경 변수]라고 띄어쓰기로 검색해야 제대로 나옵니다.2) 시스템 속성 → 고급 → [환경 변수] 클릭3) 시스템 변수 항목에서 Path를 찾아 클릭 후 편집 클릭4) 새로 만들기 → C:\Program Files\nodejs\ 입력 → 확인 클릭5) VS Code 재실행 후 커멘트 창에서 npm init 다시 실행아래 화면처럼 node.js가 정상적으로 설치되기 시작하며 오케이!![Tailwind CSS로 개발자가 만드는 멋진 UI 스타일링]✅ 강의소개 및 무료강의 보러가기 : https://inf.run/pjqKk

웹 퍼블리싱tailwindcli환경설정

Dream

[ 인프런 워밍업 클럽 Study FE 0기 ] Week 3 발자국

발자국3주 동안 진행된 스터디 클럽이 마무리되었습니다. 이번 스터디에서는 Next와 타입스크립트에 대해서 기초를 다지는 한 주를 보냈습니다. 이제 인프런 워밍업 클럽 스터디의 마지막 발자국을 남깁니다!요약Section 06-07. TDD 기본 및 간단한 앱 생성 및 배포대부분이 실습 위주였기에 코드가 궁금하다면 강의를 보자. 필요한 이론 개념만 요약하겠다.[ TDD(Test Driven Development) 란? ]TDD는 Test Driven Development의 약자로 테스트 주도 개발이라는 의미를 가지고 있다. 강의에서는 다음과 같이 설명한다.“실제 코드를 작성하기 전에 테스트 코드를 먼저 작성하는 것”테스트 코드를 작성한 후 그 테스트 코드를 Pass 할 수 있는 실제 코드를 작성한다.원하고자 하는 기능의 테스트 코드 작성 ⇒ 테스트 실행 FAIL ⇒ 테스트 코드에 맞는 실제 코드 작성 ⇒ 테스트 실행 PASSTDD는 테스트 코드를 작성한 뒤에 실제 코드를 작성한다. 단, 설계 단계에서 프로그래밍의 목적, 테스트 케이스를 작성해야 한다.요구 사항 접수요구 사항 분석 및 설계 ⇒ 목적 및 테스트 케이스 결정테스트 코드 작성FAIL (오류, 수정)이 난 코드를 테스트 케이스에 추가 후 이를 바탕으로 재 설계테스트가 통과 (PASS)된 코드만 개발 단계에서 실제 코드로 작성⇒ 반복적으로 코드의 테스트를 진행함으로서 오류 개발을 낮추고 소스 코드를 깔끔히 관리하는 것. 따라서 다음과 같은 장점이 있다.디버깅 시간 단축재 설계 시간 단축오류 발생 확률 저하추가 구현 용이테스트 기간 단축이렇게 보면 엄청나게 좋아 보이지만 모든 개발자들이 TDD 방식을 사용하지는 않는다. 아래 예시에서 첫 번째가 큰 듯 하다.익숙한 기존 방식을 버리지 못함생산성 저하 [ React Testing Library ] React Testing Library는 사용자가 컴포넌트를 사용하는 것처럼 테스팅하는 React의 테스트 라이브러리이다.React Testing Library는 React 구성 요소 작업을 위한 API를 추가하여 DOM Testing Library 위에 구축된다.DOM Testing Library란 DOM 노드를 테스트하기 위한 매우 가벼운 솔루션이다.Create React App으로 생성된 프로젝트는 React Testing Library를 지원하기 때문에 따로 설치할 필요가 없다.행위 주도 테스트(Behavior Driven Test)이다.EX) 사용자가 어떠한 행위로 이벤트가 발생되었을 때 프로그램이 어떻게 반응하는지~  [ Jest ]Jest는 현: Meta / 전: Facebook에서 만든 테스팅 프레임워크이다. 최소한의 설정으로 동작하며 Test case를 만들어서 어플리케이션 코드가 잘 돌아가는지 확인해준다Jest는 테스트 실행 환경을 제공한다.DOM이 없다(참고로 DOM 없이 React 테스트 X) ⇒ 따라서 React Testing Library와 함께 사Jest를 사용하려면 설치를 해야한다.라이브러리 설치: npm install jest —save-devTest 스크립트 변경: “test” : “jest” OR “jest —watch All”테스트를 작성할 폴더 및 파일 기본 구조 생성 Section 08. Next.js와 TypeScript[ NextJS ]Next.js란 SSR(Server-Side-Rendering)을 쉽게 구현할 수 있게 도와 주는 React 프레임워크이다.일반적으로 리액트는 SPA(Single-Page Application)를 이용해서 CSR(Client-Side-Rendering)을 하기 때문에 좋은 점도 많지만, 검색엔진 최적화(SEO)에 관한 단점이 있다. [ CSR과 SSR ]💡 사전 지식: 검색 엔진에 도움을 주는 것은 HTML의 시맨틱 태그들이다! 1. CSR (Client-Side-Rendering)React에서는 CSR 방식 기본적으로 사용CSR 초기 접속 시 렌더링 동작 방식:클라이언트가 서버에 페이지 내놔 요청서버는 빈 페이지를 클라이언트에게 전달 ⇒ 실제로 개발자 모드로 가면 HTML에 뼈대만 있는 것을 볼 수 있다.클라이언트는 JS 파일을 보고 렌더링 ⇒ 즉, 빈 페이지를 클라이언트에서 처리한다. ⇒ 서버에 대한 의존도가 별로 없다.결론: 검색 엔진에 영향을 주는 HTML이 빈 페이지니까 검색 엔진에 노출될 일이 거의 없다. [ SSR ]Next.js에서 사용하는 방식React에서도 이 방식을 사용할 수 있으나 구현이 어렵기 때문에 React로 굳이?라는 느낌.SSR초기 접속 시 렌더링 동작 방식:클라이언트가 서버에 페이지 내놔 요청서버는 미리 구성된 정적 파일을 클라이언트에게 전달. (정적 파일: HTML, CSS …)클라이언트는 전달 받은 스크립트를 실행하여 화면을 브라우저에 그림결론: 빈 페이지가 아닌 화면을 보여주기 때문에 SEO에 장점이 있다.Next.js 설치 방법 (필자가 npx만 사용해서 npx만 기록)npx create-next-app@latestnpx create-next-app@latest —typescript  [ create-next-app 기본 구조 ]Pages이 폴더 안에 페이지들을 생성만약 about이라는 페이지를 만드려면 pages폴더 안에 about.tsx를 생성해주면 된다.index.tsx가 처음 “/” 페이지로 지정된다._app.tsx는 공통되는 레이아웃을 작성한다.모든 페이지에 공통으로 들어가는 걸 넣어주려면 여기에 넣어주면 된다.url을 통해 특정 페이지에 진입하기 전 통과하는 인터셉터 페이지다. public이미지 같은 정적(static) 에셋들을 보관         styles(강의에서는 있는데 내 폴더에는 없다..) 스타일링을 처리해주는 폴더 모듈(module) css는 컴포넌트 종속적으로 스타일링하기 위한 것이며, 확장자 앞에 module을 붙여줘야한다. next.config.js Next.js는 웹 팩을 기본 번들러로 사용한다. 그래서 웹 팩에 관한 설정들을 이 파일에서 해줄 수 있다.  [ Pre-rendering ]서버에서 각 페이지의 HTML 파일을 미리 생성하는 것으로, 모든 페이지가 pre-render된다. 이렇게 하기 때문에 SEO 검색 엔진 최적화가 좋아진다.            [ Data Fetching ]Next.js에서는 데이터를 여러 방법으로 가져온다. 상황에 맞는 것을 알아서 잘 사용하자.보통 React에서는 데이터를 가져올 때 useEffect 안에서 처리한다. 하지만 Next.js에서는 다른 방법을 사용해서 가져온다. (물론 Next에서도 useEffect를 이용해 가져올 수도 있다.)getStaticProps: Static Generation으로 빌드할 때 데이터를 불러온다. (미리 한 번에 만들어 줌)getStaticPaths: Static Generation으로 데이터에 기반하여 pre-render시 특정한 동적 라우팅 구현getServerSideProps: Server Side Renderin으로 요청이 있을 때 데이터를 불러온다. [ TypeScript ]타입스크립트는 자바스크립트에 타입을 부여한 언어이다. 즉, 자바스크립트의 확장된 언어이다. 타입 스크립트는 자바스크립트와 달리 브라우저에서 실행하려면 파일을 한 번 변환해주어야 한다. 이 변환 과정을 컴파일(compile)이라고 한다.타입스크립트의 특징을 정리해보자.자바스크립트의 타입 확장 버전즉, 동적 언어: 자바스크립트 / 정적 언어: 타입스크립트이다.개발 환경에서 에러를 잡는 걸 도와준다.type annotations을 사용해서 코드를 분석할 수 있다.오직 개발 환경에서만 활성화 된다.타입 스크립트와 성능 향상은 관계없다.즉, 타입스크립트가 막 자바스크립트보다 성능이 뛰어나다!!!! 가 아니다. 자바스크립트는 굉장히 자유도가 높다. 하지만 이는 규모 큰 프로젝트를 진행한다면 오히려 독이 될 수 있다. (지옥의 타입 에러가 시작된다고 한다.) 뭐 이와 같은 이유로 타입스크립트 사용을 권장을 한다. 물론 필수 X타입스크립트는 자바스크립트를 단순화하여 더 쉽게 읽고 디버그할 수 있도록 한다.코드를 더 쉽게 읽고 이해할 수 있다.오픈 소스이다.정적 검사와 같은 자바스크립트 IDE 및 사례를 위한 매우 생산적이 개발 도구를 제공한다.자바스크립트보다 더 개선된 코드를 작성할 수 있다.ES6의 모든 이점과 더 많은 생산성을 제공한다.고통스러운 버그에서 구출해준다. [ TypeScript Type ]타입이란 컴파일러에게 "야! 나 이 타입 쓸거다?"라고 선전포고하는 것이다. 즉, 내가 어떠한 value를 사용할 것인지 추론이 가능하도록 표기하는 것. 이는 컴파일러에게도 좋겠지만 코드를 읽는 개발자도 편하다.Primitive/Object types의 경우 다음과 같다. (이건 너무 기초에 기초니까 정리 PASS)Primitive: string, number, boolean, null, undefined, symbol Object : function, array, classes, object 타입 스크립트에서는 기본적인 타입 및 특별한 타입을 제공한다.Any잘 알지 못하는 타입의 경우 사용서드 파티 데이터 로딩 시.. 도대체 뭔 데이터가 올지 모를 때 사용하면 된다.\물론 이런 "뭔 타입 올지 머르는데...?"와 같은 확실치 않은 것은 최대한 안 쓰는 게 좋다.Union또는 이다. OR이니까 | 사용한다.let code: string | number; 이렇게 하면 code의 값은 string 또는 number라는 뜻.Tuple배열에 타입 지정하는 버전~let ex: [number, string] = [1, "hello"];Enumenumerated type을 의미하며, 값들의 집합을 명시하고 이를 사용하도록 만든다.별도의 값을 설정하지 않으면 기본적으로 0 스타트이다.Void반환될 때 반환되는 데이터가 없을 경우 쓴다.참고로 함수들에서 return에 반환할 데이터 명시 안 해도 기본적으로 undefined가 반환 된다.이렇게 반환되는 값이 없어서 undefined가 반환되는 경우 void 쓰면 된다.Never아에 완전히 영원히 값이 반환할 일이 없는 경우.. 기본적으로 함수가 undefined를 반환하는데 그 조차도 하지 않는 경우에 쓴다.오류 리턴이나 영원히 끝나지 않을 무한 루프에서 쓴다.  [ annotation, type, interface ]annotation: 개발자가 타입을 타입스크립트에게 말해주는법const str: string = "Hello, World!"; const num: number = 1;type: 이 키워드를 사용해서 내가 타입 정의가 가능하다.type Person = { name: string, age: number }interface: type과 비슷하게 정의 가능~interface Person = { name: string, age: number }다만 interface는 부가적인 기능이 많다고 한다. (나중에 찾아봐야겠다.) [ Type assertion ]타입스크립트에서는 시스템이 추론 및 분석한 타입 내용을 우리가 원하는 대로 얼마든지 바꿀 수 있다. 이때 "타입 표명(type assertion)"이라 불리는 메커니즘이 사용된다.var foo = {}; foo.bar = 123; // 오류: 속성 bar가 존재하지 않음. foo.bas = "hello"; // 오류: 속성 bas가 존재하지 않음.컴파일러는 foo type이 빈 {}로 인식한다. 따라서 존재하지 않은 bar와 bas에 접근하려고 하는 것으로 보이기 때문에 당연히 오류를 낸다. 하지만 type assertion을 사용하면 이런 상황을 피할 수 있다.interface Foo { bar: number; bas: string; } var foo = {} as Foo; foo.bar = 123; foo.bas = "hello";저기 서 사용된 as가 type assertion이다.완전 간단하게 "컴파일러야. { } (왼쪽에 명시된 얘)는 Foo(오른쪽)와 같으니까 의심하지 말고 Foo(오른) 처럼 사용해라"라는 뜻이다. [ Next.js 13 ]12에서 13으로 넘어가면서 Next.js가 많이 바뀌었다고 한다! 위에서 설명한 것은 12니까 이 강의가 굉장히 중요해 보였다. (현재 14.1까지 나와따)다음 추가된 사항이고... 대부분 실습 위주 강의라서 간단하게 정리만 했다.App Routerpage 파일이 해당 경로의 페이지 컴포넌트 처리된다.Layout기본 레이아웃 정의 가능.공통된 레이아웃을 적도 children을 감싸는 형식으로 하면 된다.Server/Client ComponentNext에서는 기본이 Server Component이다.State, Hook, 이벤트 처리 및 브라우저 api 사용 => 이 경우 Client Component를 사용해야한다.코드 작성 전 맨 첫 줄에 "use client" 명시해주면 된다. Section 09. 리액트 Version 18새로 추가된 내용Automatic batchingSuspense on the server Transition등등...  [ Automatic batching ]배칭은 업데이트 대상이 되는 상태 값들을 하나의 그룹으로 묶어서 한 번의 리렌더링에 업데이트가 모두 진행될 수 있게 해주는 것이다. 하나의 함수 내부에서 여러 개의 state를 조작했을 경우 리렌더링이 그 state의 수 만큼 실행되는 것이 아니라 딱 1번만 최종 실행된다는 것이다. 그리고 제목에서 보이듯이 자동처리다. 개발자가 딱히 배칭을 위해 설정할 것은 없다.더 나은 성능을 위한 더 적은 리렌더링 가능이벤트 핸들러 밖에서도 작동필요할 때 제외 가능   [ Suspense on the server ]서버 사이드 렌더링서버에서 전체 앱에 대한 데이터 가져옴서버에서 전체 앱을 HTML로 렌더링하고 응답으로 보냄클라이언트에서 전체 앱에 대한 자바스크립트 코드 로드클라이언트에서 자바스크립트 논리를 전체 앱에 대해 서버 생성 HTML에 연결(hydration) [ Transition ]리액트에서 어떠한 업데이트가 Urgent하며 어떠한 것이 그러하지 않은지 알려준다. Section 10. 리덕스리덕스는 자바스크립트의 애플리케이션위한 상태 관리 라이브러리이다.CreateStore()앱의 전체 상태 트리를 보유하는 Redux 저장소를 만든다.앱에는 하나의 스토어만 있어야 한다.getState()애플리케이션의 현재 상태 트리를 반환한다.subscribe(listener)listener 함수 등록Action이 dispatch될 때마다 리스너 함수가 호출된다.그런 다음 getState()를 호출하여 콜백 내부의 현재 상태 트리를 읽을 수 있다. [ Provider ]<Provider> 구성 요소는 Redux Stroe 저장도에 액세스해야 하는 모든 중첩 구성 요소에서 Redux Store 저장소를 사용할 수 있도록 한다.React Redux 앱의 모든 Reqact 구성 요소는 저장소에 연결할 수 있으므로 대부분의 응용 프로그램은 전체 앱의 구성 요소 트리가 내부에 있는 최상위 수준에서 <Provider>를 렌더링한다.그런 다음 Hooks 및 연결 API는 React의 컨텍스트 메커니즘을 통해 제공도니 저장소 인스턴스에 액세스할 수 있다. [ useSelector, useDispatch ]리덕스 HOOKuseSelector스토어의 값을 가져올 수 있다.useDispatchstore에 있는 dispatch 함수에 접근. [ 리덕스 미들웨어 ]Redux 미들웨어는 액션을 전달하고(dispatch) 리듀서에 도달하는 순간 사이에 사전에 지정된 작업을 실행할 수 있게 해주는 중간자이다.💡 로깅, 충돌 보고, 비동기 API 통신, 라우팅 등을 위해 미들웨어를 사용한다!미들웨어 생성에 관한 것은 강의에서 실습으로 소개하니 궁금하면 강의를 보자. [ 커링 ]자바스크립트 고급 기술이다. (다른 언어에도 존재.)f(a, b, c)처럼 단일 호출로 처리하는 함수를 f(a)(b)(c)와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합될 수 있게 변환하는 것이다. [ Thunk ]리덕스를 사용하는 앱에서 비동기 작업을 할 때 많이 사용하는 방법이다. "thunk"라는 단어는 "일부 지연된 작업을 수행하는 코드 조각"을 의미하는 프로그래밍 용어이다. Redux 스토어의 dispatch 및 getState 메서드와 상호 작용할 수 있는 내부 로직이 있는 함수를 작성할 수 있다. [ Redux Toolkit ]Redux 툴킷은 Redux 로직을 작성하기 위한 공식 권장 접근 방식이다.Redux 코어를 둘러싸고 있으며 Redux 앱을 빌드하는 데 필수적이라고 생각하는 패키지와 기능이 포함되어 있다.Redux 툴킷은 Redux 작업을 단순화하고 일반적인 실수를 방지하고 Redux 애플리케이션을 더 쉽게 작성할 수 있다.Redux 툴킷에는 Redux Thunk가 기본으로 들어가있다. 사용 순서:configureStore을 사용하여 Redux Store 만들기React 컴포넌트에 Redux Stroe 제공 순서주위에 React-Redux <Provider>로 컴포넌트를 감싸준다.Redux Stroe를 <Provider store={store}>로 전달한다.creatSlice로 Redux 슬라이스 리듀서 생성React 컴포넌트에서 React-Redux useSelector/useDispatch Hook 사용미션과제 총 합본 https://www.inflearn.com/blogs/7021React 미션 02. 디즈니+ 클론과제 중에 가장 어려웠던 부분은 구글 연동이었습니다. 처음으로 구글 연동을 시도하면서 계속해서 오류가 발생했고, 한 번은 무한 로그인에 걸려 프로그램이 멈춘 적도 있었습니다. 이때는 정말 심장이 철렁거렸습니다. 그러나 마음을 비우고 처음부터 차근차근 시도하니 로그인 연동에 성공했습니다.또 다른 어려움은 마우스를 대면 비디오가 나오는 부분이었습니다. 이미지와 비디오를 겹쳐놓고 마우스가 hover될 때 비디오만 보여주는 부분인데, CSS의 position 이해가 부족한 것인지 배치가 제대로 되지 않아 계속 시도해야 했습니다. position에 대한 부분은 추가적인 학습이 필요하다고 느꼈습니다.React 미션 03. 포켓몬 도감포켓몬 과제는 난이도가 상이었고 디즈니와 다르게 참고할 코드조차 없는 어려운 과제였습니다. 이것은 정말 자기 스스로 머리를 쪼개가며 해결 해야하는 과제로 보였기에 가장 마지막에 해결을 하겠다 생각했습니다. 포켓몬 도감를 시작할 때, 이제야 웹을 공부를 하는 제가 이 과제를 과연 완성을 할 수 있을까? 했는데, 걱정과 다르게 디즈니보다 훨씬 쉬었습니다...! 코드를 작성하면서 렌더링이 안 일어나는 이상한 상황을 겪었지만(map key 값이 고유하지 못해서 발생한 문제) 금방 해결하고 완성을 했습니다!! 그 밖에는 문제된 일은 없습니다. 제가 스스로 작성한 코드가 작동하는 것을 보니 정말 뿌듯하네요. 그리고 예시와 다르게 UI를 상당히 많이 바꿨습니다...! 미션을 하면서 가장 즐거웠던 과제였습니다👍 React 미션 04. Next, TypeScript을 이용한 퀴즈 앱이번 프로젝트에서는 라우팅을 처리하기 위해 사용한 useRouter에서 문제가 발생했습니다. 문제를 해결하려고 찾아보니, next/router 대신에 next/navigation을 사용해야 한다는 것을 알게 되었습니다. 그것을 마지막으로 문제 없이 잘 과제를 끝 맞추었습니다.회고워밍업 스터디를 처음 시작할 때에는 HTML/CSS, JS의 기초만 약간 알고 있었고, 부트캠프나 프로젝트 경험이 없었습니다. 또한, 외적인 사정과 긴 강의 시간, 다양한 미션으로 인해 시간적으로도 부족함을 느꼈습니다. 하지만 저 자신의 발전을 위해서 새벽 3~4시까지 시간을 투자해나가며 수업과 미션을 해결해나갔습니다.또한, 저는 코틀린을 사용한 경험이 있는데, 타입 스크립트가 코틀린과 유사하다는 느낌을 받아서 타입 관련 공부가 수월했습니다. 하지만 타입스크립트의 심화 부분과 Next.js, 리덕스 등에 대해서는 추가적인 공부가 필요하다고 생각하고 있습니다.3주간의 스터디는 성장감을 느끼게 해주었습니다. 그렇기에 이미 끝났다는 사실이 아쉽기도 합니다. 이 소중한 경험을 제공해 준 인프런과 스터디를 이끌어주신 강사님에게 깊은 감사의 인사를 전합니다.앞으로도 계속해서 성장하고 발전하기 위해 노력할 것이며, 새로운 도전들을 마주하면서 더욱 성장해 나가고 싶습니다. 감사합니다! 🌱

프론트엔드워밍업클럽FE

라이프웨어

전 세계 몸값 1위 오타니를 만든 비결, '좋은 루틴 만들기와 지속하기'

10년간 연봉 9천200억원,전 세계 스포츠에서 최고의 몸값을 자랑한 '오타니'와 다저스의 계약액입니다.오타니는 이미 살아 있는 전설로 불리고 있습니다.타자와 투수 어느 하나의 최고 기량을 보여주는 것도 어려운데, 오타니는 경기에서 양 포지션 모두 최고의 모습을 보여주기 때문입니다.  오타니가 이렇게 성공했던 비결은 무엇일까요?https://www.youtube.com/watch?v=xAn5z9_AfZgSource일본 야구계에 몇 차례 책을 썼던 로버트 위팅은 '오타니는 오직 역사상 최고의 야구 선수가 되는 것에만 관심을 두는, 전사로 변신한 수도승과 같다. 목표를 위해 필요한 활동들에만 정진한다'라는 평을 남겼습니다.오타니의 이 남다른 모습은 고1로 거슬러 올라갑니다. 당시 오타니는 '8구단 드레프트 1순위'라는 목표와 이 목표를 달성하기 위한 64가지 루틴을 수립했습니다. 그리고 현재까지 실천을 이어오고 있습니다.Source: 스포츠닛폰, 오타니 쇼헤이의 만다라트 목표  오타니의 비결은 세가지로 압축됩니다.1. 장기 목표를 수립합니다. 2. 목표에 효과적인 루틴을 수립합니다.3. 꾸준히 실천합니다. '장기 목표를 수립하는 방법과 워크북'에 대해선 '[기획물] 아직 꿈과 목표를 설정 못하셨나요? 이 글 하나로 끝내세요.'를 참고해 주세요. 이번 편에선 두번째 루틴 수립법과 세번째 루틴 실천법을 다룹니다.  효과적인 루틴 수립 방법첫째, 장기 목표에 효과적인 루틴을 수립합니다.장기 목표에 부합한 루틴을 수립하는 이유는 첫번째로 꾸준한 실천을 보장하는 동기부여를 얻을 수 있기 때문입니다. 두번째로 목표와 연관된 루틴을 떠올리기 쉽고, 장기 목표가 루틴의 우선순위를 판단할 기준이 되어주기 때문입니다.Source: LifeWare 장기 목표에 부합한 루틴을 수립하는 방법으로는 '만다라트 기법'을 활용하거나 혹은 '장기목표, 비전보드, 자기암시문'의 항목을 하나씩 살피면서 연관된 루틴을 뽑고 이후 루틴들의 우선순위를 설정할 수 있습니다.(참고: 만다라트 기법의 정의와 실천 방법, 쉽게 말해 정중앙에 최종 목표(인생 한문장)을 두고 마인드맵 그리듯이 연관 습관들을 뽑아내는 아이디어 도출방식입니다.) 둘째, 벤치마킹을 활용합니다.이는 비슷한 관심사를 가진 사람들의 루틴을 참고하는 방법입니다. '타이탄의 도구들' 처럼 위인들의 루틴을 모아둔 책을 참고하는 방법도 있습니다. 혹은 '챌린저스'과 같은 루틴앱에서 내게 맞는 루틴을 발견하는 방법도 있습니다.루틴 템플릿(링크)에는 루틴 도서와 앱들을 참고하여 가능한 모든 형태의 루틴들의 리스트를 뽑고 분류해 두었습니다. 필요한 루틴들은 여기서 발견하여 수립할 수도 있습니다.Source: LifeWare 라이프 시스템 - 루틴 시스템 - 루틴 아이템 모음   꾸준한 루틴 실천 비법 3단계사실 루틴을 세워도 작심삼일하기 일쑤이지 않으셨나요? 그렇다면 '루틴 실천 방법'을 제대로 적용하고 있지 않았을 가능성이 큽니다. 이 문제는 루틴이 실행되는 단계(실행 타이밍 인지> 실행 행동 발상 > 실행 동기 판단)를 구분하여 하나씩 방법을 찾을 수 있습니다.Source: LifeWare 라이프시스템 - 루틴 시스템 - 템플릿첫번째 단계는 루틴의 '수행 타이밍'을 인지하는 것입니다.Source: LifeWare, 조건 -> 행동 관계 루틴은 '조건'과 '행동'의 쌍으로 구성되어 있습니다. 따라서 루틴 실패의 첫번째 이유는 '조건'이 명확하지 않았거나 혹은 이를 인지하지 못했을 때 발생합니다.'조건'은 크게 '시간 조건'과 '상황 조건'으로 구분합니다. '우리가 매일 x시 ~를 하겠어'라는 것은 시점과 행동을 연결시킨 '시간 조건'의 사례에 해당합니다. 반면, '나는 사람들과 대화를 하는 상황에선 ~를 하겠어'라는 것은 내가 처한 상황과 행동이 연결된 '상황 조건'의 사례에 해당합니다. Source: LifeWare 시간조건과 상황조건 예시 '시간 조건'과 '상황 조건'별로 조건을 명시하게 된다면, 해당 조건이 충족했을 때 더 잘 떠오르는 것을 느낄 수 있습니다. 또 다른 방법으로는 '알림' 등을 적극적으로 활용해 조건을 인식하는데 도움을 받을 수 있습니다. 두번째 단계는 '수행할 행동'을 인지하는 것입니다.Source: LifeWare 루틴 시스템 - 수행할 행동 인지 상승 전략루틴 실패가 발생하는 두번째 이유는 '무엇을 실천'할 것인지 잊어버리기 때문입니다. 이 경우 역시 심리적인 방법을 먼저 적용해 볼 수 있습니다. 복잡한 루틴은 일상에서 떠올리기 어렵습니다. 이때는 기억하기 용이한 형태로 재구성하는 방법을 적용할 수 있습니다. 두번째로는 기억에 대한 부담을 줄이도록 '습관 체크앱' 등의 도움을 받을 수 있습니다. 외부 툴을 사용하면 '루틴 행동'을 떠올리는 속도는 느려지나, 정확도는 향상되는 장점을 가집니다. 세번째 단계는 '동기'를 가지고 실행에 옮기는 것입니다.루틴 실패가 발생하는 세번째 이유는 '동기'가 상대적으로 부족하기 때문입니다. 즉, 루틴 수행시 '기대되는 자극'이 수행을 유발하지 못하거나 다른 것을 할 때의 '기대 자극'보다 약하기 때문입니다. 기대 자극이란 '행동을 통해 얻을 보상 및 처벌의 강도'와 '실제로 행동의 성공 가능성'이 고려된 '인지적인 동기부여의 강도'를 가리킵니다. 이를 활용해 다음 두가지 접근법을 사용할 수 있습니다.Soure: LifeWare 루틴 시스템 - 기대자극 조절 전략첫째, 루틴 수행 시 '기대 자극을 높이는 전략'입니다. 대표적으로 '장기 목표와 연결시키거나 혹은 미실시에 대한 손실을 높이는 방법'을 사용합니다.'미실시에 대한 손실을 높이는 방법'을 예로 들어 보면 '벌금을 매기거나', '그룹 수행을 통해 실시하지 않는 경우, 사회적 평판 하락'에 대한 리스크를 스스로 부과시키는 방법을 사용할 수 있습니다. 둘째, 루틴을 수행하지 않고 '다른 것을 하는 경우의 기대 자극을 낮추는 전략'입니다. 대표적으로 '최종 보상에 대한 접근성을 떨어뜨리는 방법'을 사용합니다.예를 들어, 릴스 시청 및 코인 확인 등 스마트폰 중독 증상이 있다고 합시다. 이땐 스마트폰이 잠겨서 열리지 않게하거나 물리적으로 만지기 어려운 곳에 둠으로써 스마트폰의 습관적인 사용을 낮출 수 있습니다. 즉 우리는 위에서 언급한 세가지 전략인 '수행 타이밍 인지 강화', '수행할 행동 인지 강화', '상대적인 동기 강화'를 통해 실천 가능성을 높일 수 있게 됩니다.  [정리] 단계별로 루틴 설계 방법아래에는 위에서 언급한 내용을 순서대로 따라해볼 수 있도록 정리하였습니다.1. 루틴 원페이지를 마련합니다.위 원리들이 적용된 템플릿을 활용해, 루틴을 제작하시려면 아래 다운받기를 클릭해 주세요.[ 템플릿 다운받기 >> ]  (~3/17까지 무료 제공) 2. 수행할 루틴을 기록합니다.목표를 기준으로 루틴을 뽑거나 혹은 벤치마킹 등을 활용할 수 있습니다. 그 외에도 '루틴 도서 및 앱'에 언급되었던 '루틴 전체 리스트'를 템플릿에 포함해 두었으니 참고해 주세요. 3. 루틴의 조건을 세팅합니다.루틴이 실행되는 조건을 명시합니다. '시간 조건'과 '상황 조건' 중 어디에 해당하는지 생각하고, 구체적인 조건을 명시합니다. (아래 사진에서 첫번째 칼럼)Source: Lifeware - Daily Routine 예시 4. 루틴 자체가 잘 인지되도록 만듭니다.가능하면 외부 도움을 받지 않아도 루틴이 잘 기억나도록 단순화 시키는 것이 좋습니다. 그럼에도 루틴의 정확도를 높이기 위해선 루틴 체크리스트 등의 보조 도구를 활용하는 것도 좋습니다. (위 예시 사진에서 두번째 칼럼) 5. 루틴의 기대 자극을 조절합니다.루틴이 충분히 기대자극을 불러일으키는지 검토합니다. 혹은 루틴을 방해하는 행동의 기대자극이 더 큰지 검토합니다. 보상/처벌(손실) 장치로 루틴의 기대자극을 높입니다. 그리고 방해 행동의 접근성을 떨어뜨려 기대자극을 낮춥니다. (위 예시 사진에서 세번째 칼럼)  FAQ1. 목표 및 루틴을 수립한 다음에는 어떻게 해야 할까요?다음으로는 '주기적인 피드백'을 통해 루틴을 지속적으로 업그레이드해야 합니다. 그리고 목표 중 루틴만으로 충족하기 어려운 체계적인 접근이 필요한 문제들은 프로젝트성 작업을 통해 해결합니다.1. 주기적으로 피드백을 수행합니다: Daily, Weekly 피드백을 통해 잘 지켜지지 않는 루틴들은 빠르게 확인하고 문제를 해결할 수 있습니다. 피드백한 결과를 다시 루틴에 반영하여 바로잡습니다.2. 문제해결을 통해 목표 달성을 촉진합니다: 목표에 따라서는 단계별로 체계적인 문제 해결이 필요한 경우가 많습니다. 이는 체계적인 문제 해결 방법('핵심 결과물' - '프로젝트' - '테스크')을 통해 효과적으로 수행할 수 있습니다.각 부분에 대한 상세 내용들은 추후 라이프웨어 뉴스레터를 통해 받아보실 수 있습니다. 뉴스레터를 통해 지속 가능한 성공과 성장에 도움이 되는 글들을 보내드립니다.[라이프웨어 뉴스레터 구독하기>>] 2. 루틴 관리 템플릿을 혼자 채우려니 어려워요. 어떻게 하면 좋을까요?라이프웨어에서 진행하는 '라이프웨어 올인원(All-in-One)' 프로그램을 추천드립니다. 강의도 듣고 조별 활동을 통해 실천도 하면서 4주간 나만의 라이프시스템을 완성할 수 있습니다. 이 프로그램에는 '목표, 루틴 시스템' 외에도 '피드백 시스템, 문제 해결 시스템, 문서 관리 시스템' 등 '인생을 생산적으로 살아가기 위한 필수 시스템'들을 포함하고 있습니다.이 프로그램을 3월 18일부터 4주간 온라인으로 진행될 예정입니다. 현재 3일간 모집 중이며 다음 링크를 통해 확인하실 수 있습니다.‘라이프웨어 올인원’ 1기 참여 기회 잡기 (3/13~3/15)>>  오늘의 레터를 정리합니다.1. 루틴은 꿈과 목표를 기반으로 도출되어야 한다:  꿈과 목표를 기반으로 도출된 루틴은 실행 시 동기부여가 강하다는 장점도 있으며, 지향점이 같기에 루틴 간 시너지를 창출시킬 수 있습니다.2. 루틴 실천을 위해선 '루틴 조건', '루틴 행동', '루틴 동기'를 최적화 시켜야 한다: 각 단계들이 최적화 되어 있지 않다면 '작심삼일'은 불가피하게 발생합니다. 인지적인 측면에서 솔루션과 외부 솔루션 등을 종합적으로 활용해서 실천율을 높일 수 있습니다. 위 소개된 방법을 따라할 수 있는 템플릿을 활용해 보세요. [ 템플릿 다운받기 >> ]  (~3/17까지 무료 제공)  '나만의 라이프시스템'을 함께 만들어 보는 4주간의 라이프웨어 코호트 프로그램에 참여해 보세요. 현재 1기 신청을 받고 있어요. 라이프웨어 올인원 1기는 라이프시스템 하위 시스템을 하나씩 경험하면서 삶을 변화 시킬 수 있는 프로그램으로 기획했어요. 1주차 테스크, 피드백, 루틴 관리를 시작으로 핵심 결과물과 문제 해결(프로젝트) 관리 그리고 목표 관리 등을 모두 다룰 예정이에요.프로그램을 신청을 해주세요.‘라이프웨어 올인원’ 1기 참여 기회 잡기 (3/13~3/15)>> Q. 라이프웨어는 무엇을 하는 곳인가요?삶을 변화 시키려면 무엇이 필요할까라는 고민과 연구로 이 프로그램이 탄생했어요. 자기계발 서적은 여전히 불티나게 팔리고 있지만 우리 삶은 변화가 더딘 것 같아요. 라이프웨어는 해답을 '핵심 원리가 담긴 컨텐츠와 라이프시스템'에서 찾고 있어요. 핵심 원리를 이해하고 시스템을 하나씩 적용하다보면 삶이 조금씩 변화해 가는 것을 발견하실 수 있을거에요. 그리고 꿈만 꿔왔던 목표들에 더 빠르게 가까워질 수 있을거에요.라이프웨어는 매주 뉴스레터를 통해 토픽을 하나씩 전달 드리는 것과 더불어 4주간의 집중 코호트 프로그램을 통해 시스템을 내재화하고 커스터마이징 하는 경험을 제공해 드리고 있어요. Q. 라이프웨어 코호트는 누구를 위한 프로그램인가요?자기계발 서적, 동영상 소비 대비 실제 삶의 변화가 많지 않다고 느끼는 분여러 생산성 도구를 사용하느라 불편하고 통합된 시스템을 만들고 싶은 분라이프웨어에서 소개한 컨텐츠를 혼자 적용하기에 어려움이 있고 시간이 오래 걸리는 분 Q. 프로그램의 핵심 구성은 어떻게 되나요?🙆‍♂ 적용 실습: 라이프시스템의 하위 시스템을 이해하고 실제 내 삶에 적용해 봅니다. (목표 관리, 루틴 관리, 핵심 결과물 관리, 문제 해결 관리(프로젝트), 테스크와 피드백, 지식 관리)👯‍♂ 그룹 러닝: 주 1회 온라인 그룹 러닝을 통해 자신의 시스템을 소개하고 피드백을 받습니다.🏗 DIY 제작: 스스로 노션 대시보드를 수정하는 방법을 배우고 실습합니다.📖 케이스 스터디: 라이프시스템을 성공적으로 사용하고 있는 사람들의 시스템을 분석하고 벤치마킹 포인트를 발견합니다.  라이프웨어 올인원 1기 참여 기회 잡기 (~3/15)>> 

기획 · 전략 · PM루틴생산성습관목표실천

김민구

인프런 워밍업 클럽 마지막 발자국

길다면 길고 짧다면 짧았던 약 2주간의 인프런 워밍업 클럽이 끝났다.모든 끝이 아쉽듯이 인프런 워밍업 클럽도 막상 끝이 난다고 하니 아쉽다.허나 끝은 또다른 시작이 아닌가 또다른 시작은 좋은 마무리와 함께 시작된다는 말처럼 마지막 발자국과 함께 인프런 워밍업 클럽을 잘 마무리 해보려 한다.서론이 길었다.마지막 발자국 작성을 시작하겠다. 금주는 서비스를 개발하는 것 만큼이나 중요한 개발 후 배포과정에 대해 배웠다.일별로 보자면기본적인 배포를 위한 준비AWS와 EC2 배포Spring boot 설정 , 버전업 이해하기를 배웠다. 간단한 서비스를 개발해본 적은 있지만 배포까지 해본적은 없어 배포는 어떤 과정을 거쳐 이루어지는지 궁금했는데배포의 과정을 알려주시고 배포가 왜 필요한지까지 추가적으로 알 수 있어서 정말 좋았다.배포에 관해 배웠으니 앞으론 서비스를 개발하면 개발하는 것에 그치는 것이 아니라 배포까지 해봐야겠다 (제대로 된 서비스를 개발하는게 먼저 ㅎㅎ) 추가적으로 과제에 대해 말해보자면 금주는 미니 프로젝트를 진행했는데 1단계를 마치고 2단계를 진행중에 있다.2단계는 1단계보다 어렵지만 어려운만큼 고민하는 재미와 풀어냈을 때의 쾌감이 있는 거 같다.오류가 날 때마다 그만하고 싶지만 이런 우여곡절이 나중에 다 도움이 될 것이라 생각한다 미니프로젝트는 4단계까지 존재한다.가능하면 3월달 안으론 다 풀어보고 싶다. 다 풀고 한단계 성장한 내가 될 수 있길 바란다. 마지막으로 인프런 워밍업 클럽을 진행하며 느꼈던 점을 작성하며 글을 마무리하도록 하겠다.먼저 이러한 기회를 제공해주신 인프런 분들에게 감사하다는 말을 드리고 싶다.또한 참여자들과 열정적으로 소통해주신 최태현 코치님에게도 감사하다는 말을 드리고 싶다.가벼운 마음으로 시작했던 인프런 워밍업 클럽이었는데 끝나고 돌이켜보니 내가 생각했던 것보다 훨씬 더 많은 것을 얻어가는 거 같다.지금까지 혼자 공부하며 누군가 내 방향을 잡아주면 좋겠다는 생각을 항상 했었는데 인프런 워밍업 클럽이 어느 정도 방향을 잡아준 거 같다. 인프런 워밍업 클럽이 나에게 이렇게 좋은 영향을 준 거 같이 인프런 워밍업 클럽에 참여한 분들에게도 좋은 영향을 주었길 바라며 마지막 발자국 작성을 마친다. P.S 인프런 워밍업 클럽에 참여하신 모든 분들이 잘 되시길 진심으로 바랍니다 !!   

백엔드

장서윤

[인프런 워밍업 클럽 0기] 1주차 발자국 👣

✅ 학습 내용1일차자바스크립트 기초Window 객체 및 DOMEvent2,3 일차자바스크립트 중급 4일차객체지향 프로그래밍(OOP), 비동기5일차Iterator + Generator디자인 패턴 ✅ 미션 과정1⃣ 미션 메뉴mock data 생성하면서 💡고민사항이 생겼다.[ { "name": "비빔밥", "country": "Korea", "imageUrl" : "./images/food/korea/bibimbap.png", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, ... 중략 " }, ] 💡 description 에는 무슨 내용을 넣을 것인가?해당 음식과 관련된 내용을 찾아서 넣는다로렘 입숨을 넣는다. => html, js로 기능 구현이 목표이기 때문에 아무 의미 없는 텍스트, 로렘 입숨을 넣어줬다.💡 image 폴더구조는 적절한가?폴더 구조는 항상 고민인 것 같다. 사실상 해당 프로젝트에는 food관련 image만 들어가기 때문에, images/korea/~ 등으로 food라는 폴더가 중첩해도 있지 않아도 될 것이다. 그런데 만약 다른 유형의 image가 들어간다면,,?(background image 등) 폴더 구조를 엎어야할 것이다.=> 이 부분은 개발해보면서 다른 이미지가 넣을지 말지가 결정될 때 정할 것이다!✅ 회고사실 아직 강의와 미션을 전부 완료하진 못했다... 커리큘럼 그대로 진행해야지!를 목표로 삼았으나, 계획대로 지켜지지 못해 아쉬울 따름이다. 다음 발자국은 조금 더 의미있는 내용을 기록하고 싶다.  

웹 개발워밍업FE프론트

seong2808

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

발자국강의는 진도표에 맞춰 진행하였으며 회고록 또한 그에 맞춰 작성하였다.인프런 워밍업 스터디 클럽 완주를 목표로 남은 기간동안 매일매일 꾸준히 학습하기 위해 노력하겠다.2주차 학습 내용DAY6 : 스프링 컨테이너의 의미와 사용 방법19강. UserController와 스프링 컨테이너UserController의 의아한 점static이 아닌 코드를 사용하려면 인스턴스화 필요인스터스화를 하기 위해선 생성자를 호출하게 된다.하지만 이전 강의까지 new UserController 이렇게 호출한 적이 없다!UserController는 JdbcTemplate에게 의존하고 있다.하지만 JdbcTemplate이라는 클래스를 직접 설정해준 적이 없다!→ 그 이유는 @RestController 어노테이션에게 있다. → SpringBean으로 등록해준다!SpringBean서버가 시작되면, 스프링 서버 내부에 거대한 컨테이너를 만든다.컨테이너 안에 여러 클래스가 들어간다.이때, 다양한 정보도 함께 들어있고, 인스턴스화도 이루어 진다.스프링 빈이란? 컨테이너 안에 들어간 클래스를 말한다.UserController와 다르게 JdbcTemplate은 언제 스프링 빈으로 등록하였을까?→ 의존성 설정을 할 당시에 스프링 빈으로 등록된다.스프링 컨테이너의 역할은 서로 필요한 관계에 있는 스프링 빈끼지 연결을 시켜주는 역할이다.이론 정리서버가 시작되면 스프링 컨테이너(클래스 저장고)가 시작된다.기본적으로 많은 스프링 빈들이 등록된다.우리가 설정해준 스프링 빈이 등록된다.이때 필요한 의존성이 자동으로 설정된다.왜?그렇다면 UserRepository는 JdbcTemplate을 가져오지 못할까?→ UserRepository가 스프링 빈이 아니기 때문이다.실습UserService와 UserRepository를 스프링 빈으로 등록해준 뒤 UserRepository에게만 JdbcTemplate 필요하게 되었고 controller는 service만을 호출하고 service는 repository만을 호출하여 사용할 수 있게 된다.실습상황실습한 코드의 서버가 시작하면,가장 기본적인 스프링 빈이 등록된다.JdbcTemplate을 의존하는 UserRepository가 스프링 빈으로 등록되면서 인스턴스화된다.UserRepository를 의존하는 UserService가 스프링 빈으로 등록된다.UserService를 의존하는 UserController가 스프링 빈으로 등록된다. 20강. 스프링 컨테이너를 왜 사용할까?다음 요구사항을 생각해보자책 이름을 메모리에 저장하는 API를 매우 간단하게 구현하라. Service, Repository는 Spring Bean이 아니어야 한다.구현된 그림BookController —> BookService —> BookMemoryRepository실습진행스프링 빈을 설정하지 않고 메모리에 저장한다는 가정하에 실습 진행추가 요구사항 구현메모리가 아닌 MySQL과 같은 DB를 사용해야 한다. JdbcTemplate은 Repository가 바로 설정할 수 있다고 해보자.구현된 그림BookController —> BookService BookMemoryRepository —> BookMysqlRepository하지만 Repository 뿐만 아니라 Service까지 바꿔야 한다!이런 상황에서 Repository를 다른 Class로 바꾸더라도 Service를 변경하지 않는 방법은? → Interface를 활용하자수정된 구현BookController —> BookService —> BookRepository ← BookMemoryRepository ← BookMysqlRepository실습진행인터페이스 생성하여 레포지토리가 바뀌더라고 쉽게 사용할 수 있게 실습 진행2개의 레포지토리로 인해 에러가 발생하는데 사용하는 레포지토리에 @Primary 어노테이션을 활용해서 조절하면 에러가 해결된다.하지만 Service의 변경 범위가 줄어들었지만 아쉬운 부분이 있다.그래서 등장한 것이 스프링 컨테이너이다.컨테이너를 사용하면, 컨테이너가 Service를 대신 인스턴스화 하고 그 때 알아서 Repository를 결정해준다.이런 방식을 제어의 역전(IoC, Inversion of Control)이라고 한다.컨테이너가 선택해 Service에 넣어주는 과정을 의존성 주입(DI, Dependency Injection)라고 한다. 21강. 스프링 컨테이너를 다루는 방법빈을 등록하는 방법@Configuration클래스에 붙이는 어노테이션@Bean을 사용할 때 함께 사용해 주어야 한다.@Bean메소드에 붙이는 어노테이션메소드에서 반환되는 객체를 스프링 빈에 등록한다.실습진행UserRepository에 Bean를 사용해보자@Configuration public class UserConfiguration { @Bean public UserRepository userRepository(JdbcTemplate jdbcTemplate) { return new UserRepository(jdbcTemplate); } } 언제 @Service, @Repository를 사용해야 할까?개발자가 직접 만든 클래스를 스프링 빈으로 등록할 때사실 위의 실습은 @Repository를 사용하는 것이 관례이다.언제 @Configuration + @Bean을 사용해야 할까?외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때@Component주어진 클래스를 ‘컴포넌트’로 간주한다.이 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.@Component 덕분에 ****우리가 사용했던 어노테이션이 자동감지 되었다.언제 @Component는 사용해야 할까?컨트롤러, 서비스, 레포지토리가 모두 아니고개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용되기도 한다.스프링 빈을 주입 받는 방법(가장 권장) 생성자를 이용해 주입 받는 방식setter와 @Autowired 사용 : 누군가 setter를 사용하면 오작동할 수 있음필드에 직접 @Autowired 사용 : 테스트를 어렵게 만드는 요인@Qualifier스프링 빈을 사용하는 쪽, 스프링 빈을 등록하는 쪽 모두 @Qualifier를 사용할 수 있다.스프링 빈을 사용하는 쪽에서만 쓰면, 빈의 이름을 적어주어야 한다. @Primary vs @Qualifier사용하는 쪽이 직접 적어준 @Qualifier가 이긴다. DAY7 : Spring Data JPA를 사용한 데이터베이스 조작23강. 문자열 SQL을 직접 사용하는 것이 너무 어렵다.SQL을 직접 작성하면 아쉬운 점문자열을 작성하기 때문에 실수할 수 있고, 실수를 인지하는 시점이 느리다.컴파일 시점에 발견되지 않고, 런타임 시점에 발견된다.특정 데이터베이스에 종속적이게 된다.반복 작업이 많아진다. CRUD 쿼리가 항상 필요하다.데이터베이스의 테이블과 객체는 패러다임이 다르다.그래서 등장했다.JPA (Java Persistence API) & 자바 진영의 ORM (Object-Relational Mapping)Persistence (영속성) → 서버가 재시작되어도 데이터는 영구적으로 저장되는 속성객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 보관하기 위해 Java 진영에서 정해진 규칙HIBERNATE(구현체) — 구현(Implements) —> JPAHIBERNATE은 내부적으로 JDBC를 사용한다. 24강. 유저 테이블에 대응되는 Entity Class 만들기Java 객체와 MySQL Table을 매핑할 것이다.User 객체 활용하여 실습진행User Class에 @Entity 어노테이션을 붙인다.@Entity : 스프링이 User 객체와 user 테이블을 같은 것으로 바라본다.Entity : 저장되고, 관리되어야 하는 데이터고유 id를 설정@Id : 이 필드를 primary key로 간주@GeneratedValue : primary key는 자동 생성되는 값기본 생성자 생성JPA를 사용하기 위해서는 기본 생성자가 꼭 필요하다.// 예시 protected User() {} @Column : 객체의 필드와 Table의 필드를 매핑한다.여러 옵션이 존재한다.// name varchar(20) @Column(nullable = false, length = 20, name = "name") Column은 생략 가능하다. → 옵션이 필요없고 테이블과 동일하다면 생략이 가능하다.JPA를 사용하니 추가적인 설정 필요 (한번만 하면 된다.)application.yml ****jpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql: true dialect: org.hibernate.dialect.MtSQL8Dialect ddl-auto : 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지create : 기존 테이블이 있다면 삭제 후 다시 생성create-drop : 스프링이 종료될 때 테이블을 모두 제거update : 객체와 테이블이 다른 부분만 변경validate : 객체와 테이블이 동일한지 확인none : 별다른 조치를 하지 않는다show_sql : JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여줄 것인가format_sql : SQL을 보여줄 때 예쁘게 포맷팅할 것인가dialect : DB을 특정하면 조금씩 다른 SQL을 수정해준다. 25강. Spring Data JPA를 이용해 자동으로 쿼리 날리기SQL을 작성하지 않고, 유저생성/조회/업데이트 기능을 리팩토링UserRepository 인터페이스를 User 옆에 만들어준다.JpaRepository를 상속 받는다.package com.group.libraryapp.domain.user; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { } 저장기능 → save 메서드에 객체를 넣어주면 INSERT SQL이 자동으로 날아간다.@Service public class UserServiceV2 { private final UserRepository userRepository; public UserServiceV2(UserRepository userRepository) { this.userRepository = userRepository; } public void saveUser(UserCreateRequest request) { // u에는 생성한 id가 반환된다. User u = userRepository.save(new User(request.getName(), request.getAge())); } } 조회기능 → findAll 메서드를 사용하면 모든 데이터를 가져온다.// 자바8 문법을 이용한 public List<UserResponse> getUsers() { return userRepository.findAll() .stream().map(UserResponse :: new) .collect(Collectors.toList()); } 업데이트기능Optional의 orElseThrow를 사용해 User가 없다면 예외를 던진다.객체를 업데이트해주고 save 메서드를 이용하면 UPDATE 쿼리가 날아간다.public void updateUser(UserUpdateRequest request) { // select * from user where id = ?; // Optional<User> User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); userRepository.save(user); } 이렇게 동작할 수 있는 이유?Spring Data JPA : 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리우리가 사용한 메서드들은 SimpleJpaRepository에 담겨 있다. 26강. Spring Data JPA를 이용해 다양한 쿼리 작성하기삭제 기능을 Spring Date JPA로 변경하자public void deleteUser(String name) { User user = userRepository.findByName(name); if (user == null) throw new IllegalArgumentException(); userRepository.delete(user); } findeByName 이란 SimpleJpaRepository에 존재하지 않는다. 그러므로 UserRepository에 findByName를 작성해주어야 한다.public interface UserRepository extends JpaRepository<User, Long> { User findByName(String name); } 반환 타입은 User, 유저가 없다면 null이 반환된다.find → 1개 조회By → SELECT 쿼리의 WHERE 문이 작성By 앞에 들어갈 수 있는 구정 정리find : 1개, 반환 타입은 객체 또는 Optional<타입> findAll : 쿼리의 결과물이 N개인 경우 사용. List<타입> 반환 exists : 쿼리 결과가 존재하는지 확인. 반환 타입은 boolean count : SQL의 결과 개수를 센다. 반환 타입은 long 각 구절은 And 또는 Or로 조합할 수 있다.// List<user> findAllByNameAndAge(String name, int age); SELECT * FROM user WHERE name = ??? AND age = ???;  By 뒤에 들어갈 수 있는 구절GreaterThan : 초과 GreaterThanEqual : 이상 LessThan : 미만 LessThanEqual : 이하 Between : 사이에 StartsWith : ~로 시작하는 EndsWith : ~로 끝나는 By 뒤에 들어갈 수 있는 구절 정리//List<User> findAllByAgeBetween(int startAge, int endAge); SELECT * FROM user WHERE age BETWEEN ??? AND ???;  DAY8 : 트랜잭션과 영속성 컨테이너27강. 트랜잭션 이론편트랜잭션쪼갤 수 없는 업무의 최소 단위쇼핑몰에 주문을 한다면?주문 기록 저장포인트 기록 저장주문 결제 기록 저장만약 주문 기록과 포인트 기록이 저장되고 주문 결제 기록이 저장되지 않는다면 문제가 발생한다. 이러한 문제를 해결하기 위해 모두 성공시키거나 모두 실패시키자는 개념이 등장한다.이렇게 쪼갤 수 없는 작업 단위를 트랜잭션이라고 한다.트랜잭션 시작하기start transaction; 정상 종료commit; 실패 처리rollback; 트랜잭션을 시작 후 commit이나 rollback를 하지 않으면 확인할 수 없다.이는 묶여서 저장된다는 것을 의미한다. 28강. 트랜잭션 적용과 영속성 컨텍스트@Transactional어노테이션으로 붙이면 간단히 사용가능하다.주의사항IOException과 같은 Checked Exception은 롤백이 일어나지 않는다.영속성 컨텍스트테이블과 매핑된 Entity 객체를 관리/보관하는 역할스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.특수 능력 4가지변경 감지 (Dirty Check)영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도, 변경을 감지해 자동으로 저장된다.@Transactional public void updateUser(UserUpdateRequest request) { // select * from user where id = ?; // Optional<User> User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); // userRepository.save(user); } save()를 명시적으로 하지 않아도 user.updateName()으로 user의 변경을 감지하고 save()를 하지 않아도 적용된다.쓰기 지연DB의 INSERT, UPDATE, DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번에 날린다.1차 캐싱ID를 기준으로 Entity를 기억한다.이렇게 캐싱된 객체는 완전이 동일하다.반복되는 findById(”1”)이 있다면 1번 할때, DB와 통신하여 id가 1인 무엇을 기억하고 다른 2번의 findById(”1”)은 통신하지 않고 조회할 수 있다.  DAY9 : 조금 더 복잡한 기능을 API로 구성하기30강. 책 생성 API 개발하기요구사항책을 등록할 수 있다.API 스펙 확인HTTP Method : POSTHTTP Path : /bookHTTP Body (JSON){ "anme" : String } 결과 반환 X (HTTP 상태 200 OK이면 충분)book 테이블 생성create table book ( id bigint auto_increment, name varchar(255), primary key (id) ); @Column의 length 기본값은 255문자열 필드는 최적화를 해야 하는 경우가 아닐 때 여유롭게 설정하는 것이 좋다.book 객체를 만들어 설계했던 테이블과 맵핑 시키기@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @Column(nullable = false) private String name; } BookService → loanBook() 생성@Transactional public void saveBook(BookCreateRequest request) { bookRepository.save(new Book(request.getName())); }  31강. 대출 기능 개발하기요구사항사용자가 책을 빌릴 수 있다. 다른 사람이 그책을 진작 빌렸다면, 빌릴 수 없다.API 스펙 확인HTTP Method : POSTHTTP Path : /book/loanHTTP Body (JSON){ "userName" : String, "bookName" : String } 결과 반환 X (HTTP 상태 200 OK이면 충분)user_loan_history 테이블 생성create table user_loan_history ( id bigint auto_increment, user_id bigint, book_name varchar(255), is_return tinyint(1), primary key (id) ); UserLoanHistory 객체를 만들어 설계했던 테이블과 맵핑 시키기public class UserLoanHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; private long userId; private String bookName; private boolean isReturn; } boolean으로 처리하면 tnyint에 잘 매핑된다.BookService → loanBook() 생성@Transactional public void loanBook(BookLoanRequest request) { // 1. 책 정보를 가져온다. Book book = bookRepository.findByName(request.getBookName()) .orElseThrow(IllegalAccessError::new); // 2. 대출기록 정보를 확인해서 대출중인지 확인한다. if (userLoanHitstoryRepository.existsByBookNameAndIsReturn(book.getName(), false)) { // 3. 만약에 확인했는데 대출 중이라면 예외를 발생시킨다. throw new IllegalArgumentException("진작 대출되어 있는 책입니다."); }; // 4. 유저 정보를 가져온다. User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalAccessError::new); // 5. 유저 정보와 책 정보를 기반으로 UserLoanHistory를 저장한다. userLoanHitstoryRepository.save(new UserLoanHistory(user.getId(), book.getName())); }  32강. 반납기능 개발하기요구사항유저가 책을 반납한다.API 스펙 확인HTTP Method : PUTHTTP Path : /book/returnHTTP Body (JSON){ "userName" : String, "bookName" : String } 결과 반환 X (HTTP 상태 200 OK이면 충분)이번 반납기능을 위한 API와 대출기능을 위한 API의 HTTP Body가 똑같다! 이런 경우 새로 만드는 것이 좋은지? 아니면 기존의 있는 DTO를 활용하는 것이 좋은지? 고민이 되는데 이러한 경우 새로 만드는 것을 추천한다고 한다.이유 : 만약 두 기능 중 한 기능에 변화가 생겼을 때, 유연하고 side-effect 없이 대처할 수 있기 때문이다.BookService → returnBook() 생성@Transactional public void returnBook(BookReturnRequest request) { User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalAccessError::new); UserLoanHistory history = userLoanHitstoryRepository.findByUserIdAndBookName(user.getId(), request.getBookName()) .orElseThrow(IllegalAccessError::new); history.doReturn(); } @Transactional을 사용하고 있기 때문에 영속성 컨텍스트의 변경감지기능으로 엔티티객체의 변화를 감지하여 자동으로 업데이트된다!!기능을 완성했지만 한 가지 고민할만한 내용이 존재한다.우리가 ORM을 사용하게 된 이유 중 하나는 “DB 테이블과 객체는 패러다임이 다르기 때문”이다.우리는 DISK(장기기억)와 RAM(메모리, 단기기억)의 차이 때문에 데이터의 영속성을 부여하기 위해서 DB테이블에 데이터를 저장하는 것은 필수이다.하지만 JAVA 언어는 객체지향 언어이고, 대규모 웹 애플리케이션을 다룰 때에도 절차지향적 설계보다 객체지향적 설계가 좋다!그러므로 우리는 좀 더 객체지향적으로 개발할 수 없을까?와 같은 고민을 할 필요가 있다.DAY10 : 객체지향과 JPA 연관관계 - 33강33강. 조금 더 객체지향적으로 개발할 수 없을까?현재 대출기능과 반납기능을 보면 BookService에서 User와 UserLoanHistory를 모두 끌어다가 처리하는 것을 볼 수 있다. 이러한 부분을 수정하여 같은 도메인에 속해 있는 User와 UserLoanHistory가 협업하여 BookService에서 User만 가져와 대출 및 반납을 처리할 수 있게 수정한다.선행조건 : User와 UserLoanHistory가 서로 알아야한다.@Entity public class UserLoanHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @ManyToOne private User user; private String bookName; private boolean isReturn; @ManyToOne : 내가 1이고 너가 Many이다. 즉, N:1 관계이다.N:1 관계학생과 교실을 생각하면 편하다. 학생(다수)와 교실(1)반대로 User에는 @OneToMany를 붙여주어야 한다.@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @Column(nullable = false, length = 20, name = "name") // name varchar(20) private String name; private Integer age; @OneToMany(mappedBy = "user") private List<UserLoanHistory> userLoanHistories = new ArrayList<>(); 연관관계의 주인테이블을 봤을 때, 누가 관계의 주도권을 가지고 있는가? 위 같은 경우 UserLoanHistory주도권이라는 것은 누가 상대방을 도고 있는가로 생각하면 편하다.@OneToMany(mappedBy = "user") 연관관계의 주인이 아닌 쪽에 mappedBy를 해주어야 한다.2주차 과제과제5제시된 코드를 읽어보며, 더 좋은 코드로 고쳐보기클린코드에 대해 배우고 또 알아보면서 생각보다 많은 항목을 주의할 필요가 있었다.하지만 현재 그 많은 항목들을 주의하면서 코딩하기엔 부족한 점이 많은 것 같다는 생각이 들었다. 실제로 프로젝트를 하면서 느낀 점은 시간에 쫓겨 막 작성할 때가 있는데 특히 내 경우에는 중복 코드가 발생하고 네이밍을 할때 문제가 발생한다. 이번 과제를 통해 앞으로 주의하면서 코드를 작성하고자 한다.

백엔드인프런워밍업스터디클럽발자국

김체토

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

2주 차 - 학습했던 내용 요약 디자인 시스템과 피그마 배리어블, 디자인 토큰 등에 관한 이론을 공부했습니다.배리어블 계층과 이름을 어떻게 정하는지에 대하여 익혔습니다.color, spacing, radius 등 foundation 스타일과 배리어블 등록을 했습니다. ✔ 디자인 시스템이란재사용 가능한 컴포넌트, 패턴 그리고 가이드 ✔ 디자인 시스템의 6가지 구성1. 디자인 원칙 : 디자인 시스템이 왜 필요한가 (비전, 미션, 가치 설정)2. 스타일 가이드 : 브랜드를 나타내는 스타일 가이드EX) 로고, 칼라, 레이아웃/그리드, 간격, 아이콘, 모션, 일러스트레이션, 사진, 디자인 토큰, 타이포그래피, 톤 오브 보이스3. 컴포넌트 라이브러리 : 재사용이 가능한 UI 기본 요소 (코드 포함)EX) 버튼, 인풋 필드, 체크 박스, 라디오 등4. 패턴 라이브러리 : 자주 반복 사용되는 디자인 템플릿EX) 로그인 폼, 탐색 메뉴, 컨텐츠 카드 배치5. 문서화 : 가이드라인, 컴포넌트, 패턴, 스타일 가이드 등을 문서화 한 자료6. 시스템 관리/운영 : 디자인 시스템을 효과적으로 관리하고 유지하기 위한 프로세스와 규칙EX) 업데이트, 학습 자료, 등 (=가버넌스) ✔ 디자인 시스템이 있으면 좋은 점디자인 일관성 유지, 브랜드 강화, 효율적인 개발, 시간 단축, 팀 간 협업 강화, 빠른 온보딩, 유지 보수 용이, 높은 품질의 경험 ✔ 배리어블 계층Component (=component)Alias (=semantic)Global (=primitive)Raw value  2주 차 - 회고이론을 복습하고 Foundation 만드는데 시간이 많이 걸려 컴포넌트를 아직 만들어보지 못했어요 😭조금 더 부지런히 작업을 해보겠습니다..!  3주 차 - 다짐 및 계획파운데이션 설정과 문서화를 빨리 끝내고 다음주에는 컴포넌트들을 모두 만들겠습니다.  

UX/UI인프런워밍업클럽스터디figmauiux

한지은

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

day6 - 6번 과제https://github.com/jjajan2/inflearn_study/tree/main/day6_password_generator처음에는 아스키코드 번호를 이용해서 만들려고했지만 특수문자의 경우 각각 적어줘야되기 때문에 한번에 문자열을 넣는 방식을 선택했다. range를 사용할 일이 많이 없어서 거의 처음으로 다뤄보았는데, 스타일의 경우 따로 div를 넣어서 변경해주는 방식으로 하는것같았다. 이 부분은 나중에 추가적으로 보고 적용해봐야겠다!day7 - 7번 과제https://github.com/jjajan2/inflearn_study/tree/main/day7_typing_speed7가지의 과제를 하면서 타이밍 스피트 테스트 과제가 생각보다 어렵게 느껴졌다. 한 문장을 완성하고 다음문장으로 넘어갈때 Error와 Accuracy가 누적되지않고 0으로 초기화되는 현상이 있었는데, totalErrors와 totalAccuracy를 추가해서 한 문장이 끝날때마다 누적하는 방식으로 해결했다! 생각해보면 별거 아니었던것같은데 바로 생각해내지 못해서 아쉽다.그리고 7번 타이핑 스피드 과제와 3번 퀴즈 과제에서 json파일로 데이터를 따로 빼서 사용했는데, 과제를 하던 도중 콘솔창에 에러가 생기는걸 발견했다.이 에러 메시지는 import 구문에서 assert 옵션을 사용하는 것이 더 이상 권장되지 않으며, 향후 V8 v12.6과 Chrome 126부터 지원이 중단될 것이라는 내용이고, 과거에 정적 분석을 위해 사용되었다고 한다. 이를 대신해 with을 사용하도록 권장되고있다. 라는것을 알게되었다. 하지만 fetch를 사용하는 방식으로 변경했다. 7번과제를 할때까지 몰랐던거 보면 3번과제를 할때 콘솔창을 제대로 확인하지 않았던것 같다ㅠㅠ 에러를 꼼꼼히 확인하는 습관을 기르자..!그리고 타이머를 체크하는 부분을 setTimeout과 반복문을 사용해서 만들었는데, 다 만들고나니까 코드가 허술하다는 생각이 들었다.. 그래서 setInterval을 사용하는 방식으로 변경했다!day9 - 8번 과제https://github.com/jjajan2/inflearn_study/tree/main/day9_budget_calculator예산 계산기 과제를 스터디 팀원분들과 함께 코드리뷰를 했다!총 합계를 계산하는 부분을 map을 사용해서 계산하는 코드를 작성했는데, 팀원분이 reduce를 사용하지 않은 이유를 물어보셨다..! 사실 reduce를 생각하지 못했어서 바로 reduce를 사용하는 방식으로 변경했다. (감사합니다! 😊)나도 팀원분들의 코드를 보면서 피드백을 해드리고 싶었는데, 아직 실력이 부족해서 딱히 해드릴수 있는게 없었다..확실한건 다른사람이 작성한 코드를 보는건 많은 도움이 되는것같다!인프런 스터디가 끝나도 다른 여러가지 스터디, 프로젝트를 해보면서 실력을 많이 키워야겠다

프론트엔드워밍업클럽FE1기

슬프구나

인프런 워밍업 클럽 스터디 1기 FE - 2주차 발자국

2주차는 드디어 본격적으로 리액트와 Next.js 를 하는 주간이었다! useState()useEffect()useLayoutEffect()useRef()useMemo()useCallback()useContext()useReducer()등에 다양한 훅을 복습 하였다. 리액트 훅은 16.8 에 추가가된 기능이다. 등장하면서, 클래스 컴포넌트의 시대는 저물었다. 리액트 훅 함수는 반드시 함수 컴포넌트에서만 사용해야만 한다.리액트 훅은 조건에 또는 반복문에 따라 호출이 되면 안된다.useState() 는 함수 컴포넌트내에서 상태를 관리하는 훅이다.useEffect(), useLayoutEffect 는 함수 생명주기에 대응하는 훅이다.2번째 인자로 배열을 가지는(의존성 배열) 훅들은 사용할 때 항상 주의해야한다. -> 안그러면 클로저로 인해 이전 값을 계속 참조한다.useEffect() 훅은 함수 컴포넌트가 반환하는 JSX 구문 즉 ReactElement 가 실제DOM에 렌더링 된 후 paint 후에 비동기로 동작하는 반면 useLayoutEffect() 훅은 동기적으로 페인트 이전에 실행이 된다. 리액트 함수 컴포넌트 생애주기는Render 단계와 Commit 단계로 나뉜다.Render 단계에서는 함수가 실행되어, JSX 를 반환한다. JSX 는 ReactElement 를 생성하는 함수로 변환이 되어 ReactElement 를 생성한다. 이걸 가지고 가상 DOM을 만든다. 이전에 만든 가상DOM이 있다면 이를 비교하는 작업을 한다.Commit 단계에서는 가상DOM을 실제 DOM에 반영한다.  Next.js는 React 프레임워크를 기반으로 한 웹 개발 도구로, 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG), 그리고 최근에는 클라이언트 사이드 렌더링(CSR)과 같은 다양한 렌더링 방식을 지원합니다. 각 렌더링 방식에 대한 설명과 특징을 살펴보겠습니다. 서버 사이드 렌더링 (Server-Side Rendering, SSR)개념: SSR은 각 요청이 있을 때마다 서버에서 HTML을 동적으로 생성하여 클라이언트에게 전송하는 방식입니다. 이는 초기 페이지 로딩 시 서버에서 모든 페이지를 미리 렌더링하고, 완성된 HTML을 클라이언트에게 보내줍니다.장점: 검색 엔진 최적화(SEO)에 유리하고, 초기 로딩 시 사용자에게 완성된 페이지를 보여줄 수 있어 사용자 경험이 개선됩니다.단점: 서버 부하가 늘어날 수 있으며, 렌더링 시간이 길어질 수 있습니다.정적 사이트 생성 (Static Site Generation, SSG)개념: 빌드 시 모든 페이지를 미리 HTML로 변환하여 저장합니다. 사용자의 요청에 따라 미리 생성된 HTML 파일을 그대로 전송합니다.장점: 서버 부하가 감소하고, 빠른 로딩 속도를 제공합니다. 보안이 강화되며, CDN을 통한 쉬운 배포가 가능합니다.단점: 실시간 업데이트가 필요한 경우 적합하지 않을 수 있습니다. 사이트 빌드 시간이 길어질 수 있습니다.클라이언트 사이드 렌더링 (Client-Side Rendering, CSR)개념: 초기 로딩에서는 기본적인 HTML과 JavaScript를 로드하고, 이후 모든 렌더링은 브라우저에서 JavaScript를 통해 이루어집니다.장점: 서버 부하가 줄어들고, 사용자 인터랙션에 따라 동적인 페이지 변화를 빠르게 구현할 수 있습니다.단점: 초기 로딩 시 필요한 자원이 많아져서 속도가 느려질 수 있으며, SEO에 불리할 수 있습니다.증분형 정적 재생(Incremental Static Regeneration, ISR)개념: ISR을 사용하면, 빌드 시 생성된 정적 페이지를 배포 후에도 필요에 따라 업데이트할 수 있습니다. 이는 특정 페이지를 새로운 데이터로 다시 생성하게 할 수 있는 옵션을 제공하여, 정적 사이트의 장점을 유지하면서도 동적 콘텐츠의 필요성을 충족시킬 수 있습니다. 장점성능과 캐싱: 정적 파일로 서빙되기 때문에 빠르고, CDN을 통해 캐싱될 수 있어 성능이 우수합니다.스케일링: 정적 파일을 사용하기 때문에 트래픽 증가에 따른 서버 부하가 적습니다.신선도: revalidate 옵션을 통해 정적 콘텐츠임에도 불구하고 일정 기간마다 콘텐츠를 자동으로 업데이트하여 최신 상태를 유지할 수 있습니다.단점복잡성: ISR 설정과 관리는 일반적인 SSG나 SSR에 비해 복잡할 수 있습니다.비용: 재생성 로직에 따라 추가 서버 자원이 필요할 수 있으며, 이로 인해 비용이 발생할 수 있습니다.Next.js 에서는 개발자를 위해 다양한 기능을 제공해 주고 있다.파일 기반 라우팅api 라우팅Image 최적화Metadata API등 다양해서 좋다. 그런데 프로젝트를 작성해나가다보면 예약어 파일들이 너무 많아져서 큰 회사들은 이걸 어떻게 관리하는지 궁금해진다.

프론트엔드인프런워밍업클럽FE1기회고