블로그

전다윤

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

Section - 3 역할의 분리와 스프링 컨테이너 스프링 컨테이너의 의미와 사용 방법 스프링 컨테이너와 빈?스프링 컨테이너(클래스 저장소)서버가 시작될 때 스프링 서버 내부에 만들어지는 컨테이너컨테이너 안에는 클래스가 들어가게 된다.스프링 빈스프링 컨테이너 내부의 클래스JDBC 템플릿도 스프링 빈으로 등록 되어 있다.빈을 식별할 수 있는 이름, 타입 등 다양한 정보가 저장되고 인스턴스화된다. 스프링 컨테이너를 왜 사용할까?Service의 코드를 변경하지 않고 Repository를 변경할 수 있다. (BookMemoryRepository -> BookMySqlRepository)의존성 주입(DI, Dependency Injection)컨테이너가 Service를 인스턴스화 할 때 Repository 중 하나를 선택해 넣어주는 과정제어의 역전(IoC, Inversion of Control)컨테이너가 어떤 구현 타입을 쓸지 대신 결정해준다.컨테이너가 Repository 구현체 중 하나를 선택해 Service를 만들어준다.Repositroy 주입어떤 Repository 주입시킬지 스프링 컨테이너에 명시(사용할 곳에 @Primary붙여주기)@Primary : 우선권을 결정하는 어노테이션 스프링 빈 다루기@Configuration클래스에 붙이는 어노테이션@Bean 사용 시 함께 사용한다.@Bean메소드에 붙이는 어노테이션메소드에서 반환되는 객체를 스프링 빈에 등록한다.@Component주어진 클래스를 '컴포넌트'로 간주한다.@RestController, @Service, @Repository, @Configuration 모두 내부적으로@Component를 가지고 있다.해당 어노테이션이 붙은 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.스프링 빈을 주입 받는 방법생성자 사용(@Autowired 생략 가능)setter 사용필드에 바로 사용@Qualifier 스프링 빈을 사용/등록하는 쪽 모두 사용 가능하다. 빈을 사용하는 쪽에서만 사용하면, 빈의 이름을 적어주어야 한다.양쪽 모두 사용하는 경우 @Qualifier 끼리 연결된다.@Qualifier 가 @Primary 보다 우선순위가 높다Section - 4 생애 최초 JPA 사용하기 Spring Data JPA를 사용한 데이터베이스 조작 SQL 직접 작성의 한계점작성 과정에서 실수가 발생할 수 있고 이를 인지하는 시점이 느리다.컴파일 시점에 발견되지 않고 런타임 시점에서 발견된다.특정 데이터베이스에 종속적이게 된다.반복 작업이 많아진다. 테이블 하나를 만들 때마다 CRUD 쿼리가 항상 필요하다.테이블과 객체는 패러다임이 다르다.해결책JPA (Java Persistence API) JPA자바 진영의 ORM(Object-Relational Mapping) 객체와 관계형 DB 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙 HibernateJPA의 구현체 프레임워크JPA는 인터페이스이므로 실질적인 구현 코드가 필요한데 이것이 Hibernate이다.내부적으로 JDBC를 사용  JPA 어노테이션@Entity 스프링이 User객체와 user 테이블을 같은 것으로 바라본다.Entity : 저장되고 관리되어야 하는 데이터@Id이 필드를 primary key로 간주한다.@GeneratedValueprimary key는 자동 생성되는 값MySQL의 auto_increment를 사용 = IDENTITY@Column객체의 필드와 Table의 필드를 매핑한다.null이 들어가 수 있는지 여부, 길이 제한, DB에서의 column 이름 등...생략 가능  Spring Data JPA복잡한 JPA 코드를 스프링에서 쉽게 사용할 수 있도록 도와주는 라이브러리SQL을 작성하지 않아도 쿼리가 나갈 수 있도록 자동으로 처리해준다.   트랜잭션과 영속성 컨텍스트 트랜잭션쪼갤 수 없는 업무의 최소 단위모든 SQL을 성공시키거나 하나라도 실패하면 모두 실패시킨다.@Transactional 어노테이션을 대상 메서드에 붙여 적용한다.commit : 트랜잭션 성공rollback : 트랜잭션 실패IOException과 같은 Checked Exception은 롤백이 일어나지 않는다. 영속성 컨텍스트테이블과 매핑된 Entity 객체를 관리/보관하는 역할트랜잭션 사용하면 영속성 컨텍스트 생겨나고,트랜잭션이 종료되면 영속성 컨텍스트가 종료된다. 영속성 컨텍스트의 특수 능력 4가지변경 감지(Dirty Check)  영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도, 변경을 감지해 자동으로 저장된다.쓰기 지연(Lazy Loading)DB의 INSERT / UPDATE / DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번만 날린다.통신 횟수가 줄어든다.1차 캐싱ID를 기준으로 Entity를 기억한다.이렇게 캐싱된 객체는 완전이 동일하다. 조금 더 복잡한 기능을 API로 구성하기HTTP Body 스펙이 동일할 때, DTO를 새로 만들어야 할까?새로 만드는 것이 좋다두 기능 중 한 기능에 변화가 생겼을 때 side-effect 없이 대처할 수 있기 때문이다.   과제[과제 #4] API 개발MYSQL DB 데이터를 저장하고 데이터를 처리해볼 수 있었다. 그리고 이를 Postman을 이용해 확인해 볼 수 있었다.[과제 #5] 클린 코드지난주에는 스프링과 DB에 대한 기본적인 내용을 학습했다면 이번 주에는 스프링의 핵심 개념에 대해 알 수 있었다. 그래서 이전의 수업들에 비해 수업 내용을 이해하는 데 더 많은 시간이 필요했던 것 같다. 그리고 과제 5와 특강을 통해 클린 코드와 테스트의 중요성에 대해서 다시 한번 느낄 수 느낄 수 있었다. 이번 주에는 여유가 없어서 주어진 강의와 과제만 진행했는데, 다음 주에는 배운 내용에 대해서 추가적인 공부를 할 수 있었으면 좋겠다.

백엔드인프런워밍업클럽1기BE

변지섭

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

이번 주는 학교 과제를 완료하고, 포트폴리오를 작성한다고 강의를 많이 듣지 못했습니다. 23-25강 내용을 요약하고, 추가로 공부하며 느낀 점을 말씀드리겠습니다.  학습 내용 요약:Section 4: 생애 최초 JPA 사용하기ORM, 스프링 JPA, Spring Data JPA가 무엇인지 이해ORM이 Object Relational Mapping이라는 것은 알고 있었는데, 그래서 구체적으로 어떻게 객체와 테이블을 매핑하는지는 생각해보지 못했습니다.강의에서 객체와 관계형 DB는 패러다임이 다르다고 하셨고, 대표적으로 연관관계나, 상속을 표현하지 못한다고 하셨습니다. 예를 들어, 테이블 1이 테이블 2과 단방향으로 참조하는 경우는 많으나, 양방향 참조를 하는 경우는 많지 않습니다. 하지만 객체의 경우에는 양방향 참조가 자유롭습니다.그리고 Join을 이용하면 상속과 비슷한 효과를 낼 수 있겠지만, 객체를 상속하는 것과 완전히 동일하지는 않을 것입니다.이러한 이유로 객체와 관계형 DB를 짝지어주는(매핑하는) ORM이 등장하게 되었습니다.추가적으로, 연관관계라는 내용이 JPA에만 해당하는 내용일까 검색을 해 보았는데, 다른 ORM들에서도 연관관계(Association)을 지원하고 있습니다.SQLAlchemy(Python ORM): https://docs.sqlalchemy.org/en/20/orm/basic_relationships.htmlSequelizer(Typescript ORM): https://sequelize.org/api/v7/classes/_sequelize_core.index.association 다만, 자바의 경우 객체 개념이 매우 중요하기 때문에, ORM의 중요도가 더 크겠다는 생각이 들었습니다.추가적으로, Persistent 하다는 것도 좀 더 조사해보았습니다.자바는 객체들을 메모리(RAM)에 저장하는데, RAM은 전원이 꺼지면 저장된 데이터가 사라집니다. 그래서 영구적으로 데이터를 저장하려면 보조기억장치(SSD, HDD)에 저장해야 합니다. JPA에서 Persistent 하다는 것은 영구적으로 저장된다 즉, DB(가 관리하는 보조기억장치)에 저장된다는 것을 의미합니다. 그리고 이때, JPA는 어노테이션이나 XML을 활용해 ORM 표준을 정의하므로, DB 종류에 영향을 받지 않습니다. JPA는 EntityManager API를 활용해 저장(persist), 업데이트(update), 조회(retrieve), 제거(remove)를 수행합니다.EntityManager는 현재 애플리케이션이 사용 중인 엔티티 객체를 관리하며, DB와 상호작용, ORM 메타 데이터도 관리합니다. EntityManagerFactory에 의해 생성됩니다.엔티티 객체란, 데이터베이스 테이블의 한 행을 나타내는 자바 클래스입니다. 자신의 필드를 통해 상태를 유지합니다.Reference: https://www.ibm.com/docs/en/was-liberty/nd?topic=liberty-java-persistence-api-jpa그리고 Spring Data JPA와 JPA가 다르다는 것도 처음 알았습니다 ;)JPA도 물론 객체와 관계형 DB의 매핑을 단순화한 것이지만, Spring Data JPA를 활용하면 더 쉽게 사용할 수 있습니다. Spring Data JPA, JPA, Hibernate, JDBC강의에서 "Spring Data JPA는 JPA를 사용하고, JPA를 구현한 Hibernate(구현체)는 JDBC를 사용해 DB와 통신한다." 라고 말씀하신 부분이 전체 흐름을 보여주는 것 같습니다.  스터디에 임하는 자세:스프링을 공부한지 이제 2주차가 됩니다. 이번 주에는 JPA를 이해하기 위해 ibm 독스를 읽고 생각하는 과정이 너무 즐거웠습니다. 인프런 스터디를 마치더라도 이렇게 깊게 공부하고 글로 정리하는 습관을 유지해야겠습니다. 아직 람다 함수, 메서드 레퍼런스 내용이 생소한데, 다음주에 이 주제에 대해서도 깊게 공부해서 정리하겠습니다.또, 이번 주에 TDD 책을 공부해보고 싶어서 1장을 공부해보았는데, 완전히 이해는 하지 못한 것 같습니다. 그래도 "테스트를 작성한다 -> 컴파일이 되게 만든다 -> 어떻게든 돌아가게 만든다 -> 리팩토링한다"의 반복이라는 점은 기억하고 있습니다. 그리고 단위 테스트 코드가 통과해서 초록막대를 보는 것도 즐거웠습니다 🙂 지금은 태현님 강의를 완강하고, 이후 미니 프로젝트에 TDD를 적용해봐야겠습니다.

dev_traveler

[인프런 워밍업 스터디 1기 디자인] 2주차 입력 컴포넌트, 디스플레이 컴포넌트

드디어 Figma의 꽃이라고 할 수 있는 컴포넌트 만들기를 시작했습니다. 익숙해지면 너무 쉽고 당연한 것들이지만 처음에 익숙해지기까지 정말 헷갈리고 복잡합니다. 그러나 컴포넌트를 하나 둘 따라 만들다 보니 점점 속도도 빨라지고 재미있었습니다. 컴포넌트 만드는 요령1. 현재 알고 있는 모든 프로퍼티를 가진 가장 복잡한 형태의 컴포넌트를 만든다.어떤 컴포넌트들은 아주 복잡해서 프로퍼티가 아주 많을 수 있습니다. 프로퍼티들을 모두 활성화 했을때의 가장 복잡한 형태의 컴포넌트를 만들어봅니다. 2. 모든 프로퍼티를 등록해준다. 3. 인스턴스를 만들어서 테스트하면서 기본 컴포넌트를 수정한다.다양한 Variants 를 만들기 전에 꼭 인스턴스를 만들어서 기본 컴포넌트를 테스트해보는것이 좋습니다. 여러 프로퍼티들을 껐다 켜보고 크기도 늘렸다 줄여보고 할 수 있는건 다 해봅니다. 테스트해보면 꼭 생각하지 못한 오류가 발견되는데 개발할 때랑 비슷해서 재밌었습니다. 컴포넌트가 작을 때 수정해야 여러 컴포넌트를 조합해서 복잡한 컴포넌트를 만들 때 고생하지 않습니다. (단위 테스트의 중요성!) 새롭게 알게된 것디자인 패널에 Absolute positioning 버튼을 누르면 position: absolute 로 만들 수 있습니다!Constraints 에서 Left and right 를 해주면 부모요소가 늘어남에 따라 자식요소도 함께 늘어납니다!Constraints 에서 Bottom 을 해주면 하단에 요소를 고정시킬 수 있습니다. 부모가 Auto Layout 이면 자식요소의 Contraints 적용이 불가능합니다.부모 요소의 width 를 Fixed 로 하고 자식 요소의 width 를 Fill 로 설정하면 부모 요소의 크기가 변할 때 마다 자식 요소가 늘어납니다.

UX/UIfigmadesign_systemcomponent

임하스

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

시작강의가 좀 밀려있긴 한데... 기간 안엔 다 들을 수 있을 거라 생각하며 차근차근 정리 중이다...정리자바스크립트 this 키워드메서드 내 this ⇒ 해당 객체(Object)함수 내 this ⇒ window 객체생성자 함수 내 this ⇒ 빈 객체(해당 인스턴스)forEach문 내 this ⇒ 두 번째 매개 변수 값, 없을 경우 window 객체 또는 해당 객체화살표 함수 내 this ⇒ 상위 스코프의 this예제메서드 내 ⇒ 해당 객체(Object)const audio = { title: 'a', play() { console.log('play this', this); }, }; audio.stop = function () { console.log('stop this', this); }; audio.play(); //play this { title: 'a', play: f } audio.stop(); //play this { title: 'a', play: f } 함수 내 ⇒ window 객체function playAudio() { console.log(this); } playAudio(); // Window 객체 생성자 함수 내 ⇒ 빈 객체(해당 인스턴스)function Audio(title) { this.title = title; // 이게 없으면 빈 객체 console.log(this); } const audio = new Audio('a'); // Audio { title: 'a' } forEach문 내 ⇒ 두 번째 매개 변수 값, 없을 경우 window 객체 또는 해당 객체const audio = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach(function (category) { console.log(`title: ${this.title}, category: ${category}`); // 함수 내에 있기 때문에 window 객체를 가리킴 }); }, }; audio.displayCategories(); const audio2 = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach( function (category) { console.log(`title: ${this.title}, category: ${category}`); }, { title: 'audio' } ); // forEach 두 번째 매개변수에 있는 값을 참조시킬 수 있음 }, }; audio2.displayCategories(); const audio3 = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach( function (category) { console.log(`title: ${this.title}, category: ${category}`); }, this // audio3 해당 객체를 참조 ); }, }; audio3.displayCategories(); 화살표 함수 내 ⇒ 상위 스코프의 thisconst audio = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach(function (category) { console.log(`title: ${this.title}, category: ${category}`); // 상위 스코프의 this(Lexical this) => audio { title : 'audio', categories: Array(3) } }); }, }; audio.displayCategories(); call, apply, bind함수에서 this 사용 시, window 객체가 아닌 값을 참조시키기 위해 사용함수명.call(thisArg[, arg1, arg2, ...]) : 함수를 호출하는 함수함수를 호출할 때, 첫 번째 매개 변수에 함수 내에서 사용할 this 값을 지정할 수 있음첫 번째 매개 변수로 null 또는 undefined가 전달되면 전역 객체가 this로 사용됨두 번째 ~ … 매개 변수로 호출할 함수에 전달될 인수 추가 가능const fullName = function (arg1, arg2) { console.log(`${this.firstName} ${this.lastName}`); console.log(`${arg1} ${arg2}`); }; const person = { firstName: 'Im', lastName: 'Hass', }; fullName.call(person, '매개변수1', '매개변수2'); // Im Hass // 매개변수1 매개변수2 함수명.apply(thisArg[, argArray]) : call()과 동일call()과 인수를 배열로 넣어주는 차이const fullName = function (arg1, arg2) { console.log(`${this.firstName} ${this.lastName}`); console.log(`${arg1} ${arg2}`); }; const person = { firstName: 'Im', lastName: 'Hass', }; fullName.apply(person, ['매개변수1', '매개변수2']); **함수명.bind(thisArg)**: 함수가 실행되지 않고 바인딩만 시켜줌함수를 바인딩할 때, 새로운 함수를 생성함 → 원본 함수와 동일한 코드를 가지지만 this가 바인딩된 값이 들어감 ⇒ 새로운 변수에 담거나 즉시 실행 시켜서 사용해야 함function func(language) { if (language === 'kor') { console.log(`language: ${this.korGreeting}`); } else { console.log(`language: ${this.engGreeting}`); } } const greeting = { korGreeting: '안녕', engGreeting: 'Hello', }; // 원본은 변하지 않기 때문에 undefined 출력 // func.bind(greeting); // func('kor'); // language: undefined // 1. 새 변수에 담아서 사용하기 // const boundFunc = func.bind(greeting); // boundFunc('kor'); // language: 안녕 // 2. 즉시 실행하기 func.bind(greeting)('kor'); Event Loop자바스크립트는 동기 언어지만, 다른 것의 도움(브라우저의 자바스크립트 엔진 또는 Node.js와 같은 런타임 환경)을 통해 비동기로 처리할 수 있음.ex) setTimeout(브라우저 api-window object 또는 node api-global object)setTimeout에 설정된 값이 0(즉시 실행)이어도 콜백 큐에서 호출 스택이 비어야 들어가기 때문에 순서가 다르게 출력됨자바스크립트 코드를 실행하기 위해 엔진이 필요한데, 자바스크립트 엔진에는 메모리 힙, 콜 스택 두 가지 주요 구성 요소가 있음Call Stack(호출 스택)코드 실행에 사용되는 메모리 구조함수 호출 및 반환을 추적함코드가 실행될 때 함수 호출은 스택에 추가되고, 함수가 완료되면 스택에서 제거됨Callback Queue(콜백 큐)비동기 작업의 콜백 함수들이 대기하는 대기열비동기 작업이 완료되면 해당 콜백 함수가 콜백 큐에 추가됨Event Loop(이벤트 루프)콜 스택과 콜백 큐를 계속 모니터링하여 콜 스택이 비어있을 때 콜백 큐의 첫 번째 콜백을 콜 스택으로 이동시킴내부 진행 참고 사이트 : https://kamronbekshodmonov.github.io/JELoop-Visualizer/Closure외부 함수 보다 생명 주기가 긴 내부 함수 중에서, 종료된 외부 함수의 값을 참조할 수 있는 내부 함수를 의미한다.클로저 사용 전(오류: b에 접근 불가)let a = 'a'; function funcB() { let c = 'c'; console.log(a, b, c); } function funcA() { let b = 'b'; console.log(a, b); funcB(); } funcA(); // a b // ReferenceError: b is not defined 클로저 사용 후(해결: 내부 함수로 변경)let a = 'a'; function funcA() { let b = 'b'; console.log(a, b); function funcB() { let c = 'c'; console.log(a, b, c); } funcB(); } funcA(); // a b // a b c 구조 분해 할당배열이나 객체의 속성을 해체하여 개별 변수에 담을 수 있게 하는 Javascript 표현식객체 구조 분해 할당let person = { name: 'hass', age: 30, phone: '123', address: { zipcode: 1234, street: 'rainbow', number: 42, }, }; let { address: { zipcode, street, number } } = person; console.log(zipcode, street, number); 별명 지정let people = [ { name: 'mike', age: 35, family: { mother: 'Jane', father: 'Harry', sister: 'samantha', }, }, { name: 'Tom', age: 25, family: { mother: 'Norah', father: 'Richard', brother: 'Howard', }, }, ]; for (let { name: n, family: { father: f }, } of people) { console.log(`Name : ${n}, Father: ${f}`); } // Name : mike, Father: Harry // Name : Tom, Father: Richard Map() 메서드배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환함array.map(callback(currVal[, index[, array]]])[, thisArg])첫 번째 매개 변수로 callback 함수가 들어가고, 두 번째 매개 변수에 this 바인딩 가능Filter() 메서드주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환함array.filter(callback(element[, index[, array]])[, thisArg])첫 번째 매개 변수로 callback 함수가 들어가고, 두 번째 매개 변수에 this 바인딩 가능Reduce() 메서드배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환함arr.reduce(reducerFunction(acc, curr, idx, src), initalValue)reducerFunction 4개의 인자를 가짐누산기(acc)현재 값(curr)현재 인덱스(idx)원본 배열(src)예제const result = [0, 1, 2, 3, 4].reduce(function (acc, curr, idx, src) { return acc + curr; }, 10); // console.log(result); // 20 acc curr idx src return value 1번째 호출 10 0 0 [0, 1, 2, 3, 4] 10 2번째 호출 10 1 1 [0, 1, 2, 3, 4] 11 3번째 호출 11 2 2 [0, 1, 2, 3, 4] 13 4번째 호출 13 3 3 [0, 1, 2, 3, 4] 16 5번째 호출 16 4 4 [0, 1, 2, 3, 4] 20  undefiend vs null공통점둘 다 원시 자료형undefiend의 타입은 undefiend, null 타입은 null(object로 나옴, strit 모드는 null) 유일한 타입undefined아무 값도 할당받지 않은 상태개발자의 의도 X, 자바스크립트 엔진이 초기화한 값null비어있는 값, 존재하지 않는 값개발자가 의도한 값null을 할당하면 변수가 이전에 참조하던 값을 명시적으로 참조하지 않겠다고 하는 것이므로, 자바스크립트 엔진이 변수의 메모리 공간에 대해 가비지 콜렉션을 수행함얕은 비교(Shallow Compare)숫자, 문자 등 원시 자료형은 값으로 비교배열, 객체 등 참조 자료형은 참조되는 위치를 비교IdentifiersuserName seasons isFinished enemies userCall StackAddress Value 0012ASF “Hass” 3241AF 5 416UHDI true 1235JFT HHSYDW1462 JFFI12HA KHS18205JAHeapAddress Value HHSYDW1462 ["ene1", "ene2", "ene3"] KHS18205JA {name: "hass", profession: "Drug dealer"}깊은 비교(Deep Compare)객체의 경우에도 값으로 비교Object Depth가 깊은 경우 : lodash 라이브러리의 isEqual() 사용Obejct Depth가 깊지 않은 경우: JSON.stringfy() 사용const obj1 = { a: 'a', b: 'b' }; const obj2 = { a: 'a', b: 'b' }; console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true 얕은 복사, 얕은 동결얕은 복사 : 내부에 중첩된 값은 복사되지 않음(기존과 동일한 객체를 참조함, 하나를 변경하면 똑같이 변경됨)얕은 동결 : Object.freeze() 메서드, 객체를 동결하여 변경할 수 없도록 함.얕은 복사를 하는 연산자 : 전개 연산자, Oject.assign(), Array.from(), slice()깊은 복사내부 중첩된 값까지 복사함JSON.parse(JSON.stringify(object)) 또는 중첩된 부분에 전개 연산자 사용 { …aObj, cObj: {…aObj.cObj }}lodash 라이브러리를 활용structuredClone(object) 메서드 활용함수 선언문, 함수 표현식함수 표현식 : 함수를 만들고 변수에 할당, 익명 함수호이스팅 시, 함수 선언식만 영향을 받음함수 표현식은 인터프리터가 해당 코드 줄에 도달할 때만 로드됨함수 표현식은 정의된 로컬 변수의 복사본을 유지할 수 있도록 호이스팅 되지 않음즉시 호출(실행) 함수 표현식(IIFE(Immediately Invoked Function Expression))정의 하자마자 즉시 실행되는 함수를 의미함기본 형태 ( function () {} )()첫 번째 소괄호( ... ) : 전역 선언을 막고, IIFE 내부로 다른 변수의 접근을 막음두 번째 소괄호 …() : 즉시 실행 함수를 생성하는 괄호, 이를 통해 Javascript 엔진이 함수를 즉시 해석하고 실행함IIFE를 변수에 할당하면 IIFE 자체는 저장되지 않고, 함수가 실행된 결과만 저장할 수 있음IIFE로 Closure가 가능하게 만든 후, 내부 함수에서 외부 함수에 있는 변수에 접근되게 함사용 목적변수를 전역으로 선언하는 것을 피하기 위해IIFE 내부에 다른 변수들이 접근하는 것을 막기 위해익명 함수를 만들기 위해 (이름이 없기 위한 두 가지 조건 중 한 가지)이 함수를 할당 받을 변수를 지정해야 함이 함수를 즉시 호출해야 함// 이름은 선택 사항이라고 했지만 function 키워드로 만들 때 에러 발생 // function (a, b) { // Error: Identifier expected. // return a- b; // } // 조건1 const minus = function (a, b) { return a - b; }; // 조건2 (function (a, b) { return a - b; })(1, 2); IIFE 적용 전const score = () => { let count = 0; return { current: () => { return count; }, increment: () => { count++; }, reset: () => { count = 0; }, }; }; console.log(score); // () => {...} 함수 출력 console.log(score().current()); // 0 score.increment(); // count를 1 증가 시킴 console.log(score().current()); // 0 => score()를 다시 실행 시켜서 count가 0으로 초기화됐기 때문에 값이 증가하지 않음 IIFE 적용 후const score = (() => { let count = 0; return { current: () => { return count; }, increment: () => { count++; }, reset: () => { count = 0; }, }; })(); console.log(score); // { current: f, ... } console.log(score.current()); // 0 score.increment(); // count를 1 증가 시킴 console.log(score.current()); // 1 Intersection observer무한 스크롤, 이미지 lazy loading 등을 구현할 때 사용됨Lazy Loading 예제<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Intersection Observer</title> <style> img { width: 400px; height: 300px; display: block; margin: 10px auto; } </style> </head> <body> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <script> const observer = new IntersectionObserver( function (entries, observer) { console.log('entries', entries); entries.forEach((entry) => { if (entry.isIntersecting) { // 루트 요소와 타겟 요소가 교차하면 console.log('옵저버'); entry.target.src = entry.target.dataset.src; // dataset에 있는 url을 src에 넣어줌 observer.unobserve(entry.target); // Lazy loading 후에는 observer 해제 } }); }, { threshold: 1 } // 타겟 요소가 루트 요소와 100% 겹치면 콜백 함수 실행 ); const imgEls = document.querySelectorAll('img'); console.log(imgEls); imgEls.forEach((img) => { observer.observe(img); }); </script> </body> </html> 순수 함수(Pure Function)함수형 프로그래밍 패러다임의 한 부분특정 함수가 다른 함수에 미치는 예기치 못한 영향을 최소화하고, 함수를 만들고 실행할 때 어떤 결과값을 리턴할지 예측할 수 있다는 장점이 있음가능한 순수 함수를 사용하는 것이 좋음클린 코드를 위해서테스트를 쉽게 하기 위해서디버그를 쉽게하기 위해서독립적인 코드를 위해서두 가지 규칙이 있음같은 입력이 주어졌을 때, 언제나 같은 결과값을 리턴한다 (same input → same output)사이드 이펙트를 만들지 않는다 (동일한 input이지만 외부 값에 의해 변경되는 output → impure하게 만듦)커링(Curry Function)함수와 함께 사용하는 고급 기술 (다른 언어에도 존재하는 기술)f(a, b, c) 처럼 단일 호출로 처리하는 함수를 → f(a)(b)(c)와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합될 수 있게 변환하는 것즉, 함수를 호출하는 것이 아닌 변환하는 것예제const sum = (x, y) => x + y; console.log(sum(10, 20)); // 30 const curriedSum = (x) => (y) => x + y; console.log(curriedSum(10)); // f() y => x + y console.log(curriedSum(10)(20)); // 30 const makeFood = (ingredient1) => { return (ingredient2) => { return (ingredient3) => { return `${ingredient1} ${ingredient2} ${ingredient3}`; }; }; }; // const hamburger = makeFood('Bread')('Ham'); // console.log(hamburger); // (ingredient3) => {...} 완성되지 않음 const hamburger = makeFood('Bread')('Ham')('Tomato'); console.log(hamburger); // Bread Ham Tomato => 3개의 인수를 다 넣어줘야 완성됨 // makeFood 축약 const cleanMakeFood = (ingredient1) => (ingredient2) => (ingredient3) => `${ingredient1} ${ingredient2} ${ingredient3}`; const newHamburger = cleanMakeFood('Bread')('Ham')('Tomato'); console.log(newHamburger); // Bread Ham Tomato // 일반 log 함수 function log(date, importance, message) { alert(`[${date.getHours()} : ${date.getMinutes()}]: [${importance} ${message}]`); } log(new Date(), 'DEBUG', 'same bug'); // 커리 함수 function curry(f) { return function (a) { return function (b) { return function (c) { return f(a, b, c); }; }; }; } const curriedLog = curry(log); curriedLog(new Date())('DEBUG')('same bug'); // 동적으로 추가되는 커리 함수 (매개 변수 개수 상관 없이 커리 함수로 변경 가능) function curry2(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this.args); } else { return function (...args2) { return curried.apply(this, args.concat(args2)); }; } }; } // const sum = (x, y, z) => x + y + z; const sum = (x, y, z, j) => x + y + z + j; const curriedSum = curry2(sum); // console.log(curriedSum(1)(2)(3)); console.log(curriedSum(1)(2)(3)(4)); strict modeJavascript의 엄격 모드제한된 버전을 선택하여 암묵적인 “느슨한 모드(sloppy mode)”를 해제하기 위한 방법시멘틱스에 몇 가지 변경이 일어남기존에는 무시되던 에러를 throwingJavascript 엔진의 최적화 작업을 어렵게 만드는 실수를 바로 잡음. 가끔 엄격 모드의 코드가 비엄격 모드와 동일한 코드 보다 더 빠르게 작동하도록 만들어줌.적용 방법파일에 "use strict" 지시자 입력함수 안에 "use strict" 지시자 입력하여 해당 함수만 적용 가능class를 사용하면 자동으로 strict 모드 적용자바스크립트 파일의 타입을 module로 설정하면 strict 모드 적용<script src="script.js" type="module">기존과 차이점할당할 수 없는 값에 할당하려 할 때 ex) 선언되지 않은 변수, undefiend, NaN수정할 수 없는 값을 수정하려 할 때 ex) readOnly 객체동일한 이름의 파라미터가 존재할 때함수 내의 this가 undefiend가 됨

프론트엔드인프런워밍업스터디클럽FE1기발자국

김동현

[인프런 워밍업 클럽 1기/BE] 두번째 발자국

스프링 컨테이너스프링이 시작될 때스프링 서버 내부에 거대한 컨테이너를 만듬 컨테이너에 들어가는 -> 클래스 -> 들어간 클래스 = 스프링 빈@RestController - Controller 클래스를 API 진입 지점으로 만들고 스프링 빈으로 등록시킴@Repository - Repository를 스프링 빈으로 등록할 때 사용@Service - Service를 스프링 빈으로 등록할 때 사용 스프링 컨테이너 사용 이유제어의 역전(IoC, Inversion of Control) - 컨테이너가 Repository 중 하나를 선택한 후, Service를 만듬. -> Service를 수정하지않고 Repository만 수정하면됨의존성 주입(Dependency Injection) - 컨테이너가 Service를 만들어 줄때 Repository 중 하나를 선택해서 넣어주는 과정@Primary 어노테이션을 사용해서 우선순위 설정 스프링 컨테이너에 빈 등록 방법@Configuration + @Bean 을 사용일반적으로 개발자가 직접 만든 클래스를 등록할떄는 @Service, @Repository를 사용외부 라이브러리나 프레임워크에서는 @Configuration + @Bean을 사용@Component - 클래스를 컴포넌트로 간주하고 스프링 서버가 뜰 때 자동으로 감지, 위의 어노테이션 모두 컴포넌트 어노테이션을 포함함(컨트롤러, 서비스, 레포지토리 아닌 추가적인 클래스 등록할 때 종종 사용)스프링 빈 주입받는 방법생성자를 이용해 주입(권장 방식)setter 사용필드에 직접 주입방식 JPA(Java Persistence API)문자열로 SQL을 작성하면 실수하기 쉽고, 실수를 늦게 발견하는 경우가 많음자바 진영의 ORM(Object-Relational Mapping)Hibernate로 구현Hibernate는 내부적으로 JDBC 사용 저장: save 메소드를 이용해 새로운 객체를 데이터베이스에 저장조회: findAll 메소드로 모든 유저 데이터를 조회업데이트: findById를 사용해 특정 유저를 찾은 후, 업데이트를 진행삭제: delete 메소드를 이용하여 데이터를 삭제추가적인 쿼리 작성법: findBy, findAllBy, existsBy, countBy 등 다양한 접두사를 사용하여 복잡한 쿼리도 간단하게 작성할 수 있습니다. 조건 연산자(예: GreaterThan, LessThan 등)를 이용해 더 세밀한 쿼리도 가능트랜잭션 개념트랜잭션 정의: 쪼갤 수 없는 업무의 최소 단위필요성: 여러 작업을 한 묶음으로 처리하여 모두 성공하거나 실패하게 하는 것 트랜잭션 필요 예시인터넷 쇼핑몰에서 주문, 포인트 적립, 구매 기록 저장 시 모든 과정이 함께 성공하거나 실패해야 함 트랜잭션 적용 방법SQL 명령어: start transaction; 후 commit; 또는 rollback;으로 처리코드 적용: @Transactional 어노테이션 사용 영속성 컨텍스트정의: Entity 객체를 관리/보관하는 역할특성: 트랜잭션 시작 시 생성되고 종료 시 소멸 영속성 컨텍스트 기능변경 감지(Dirty Check): Entity의 변경 사항 자동 감지 및 저장쓰기 지연: 트랜잭션 commit 시점에 모아둔 SQL을 한 번에 처리1차 캐싱: 동일 ID 조회 시 최초 한 번만 SQL 실행, 이후 캐시 데이터 활용트랜잭션 및 영속성 컨텍스트 적용 예@Transactional 사용하여 UserService에 트랜잭션 적용조회 시 readOnly=true 옵션으로 최적화 가능 과제 제출4일차 과제5일차 과제2주차 회고스프링 컨테이너에 대한 이해가 힘들었다. 스프링을 처음 사용한 건 아니라서 제어 역전, 의존성 주입등의 개념은 들어본적있지만 그것이 정확하게 어떤 개념인지 모르고 사용했었는데 강의를 통해서 조금이나마 이해된 것 같다. 스프링 컨테이너부분을 몇번씩 돌려본 것 같다.... 어렵다.. 이부분을 좀더 반복해서 들어야겠다. JPA도 역시 직접 sql 쿼리를 날려주는 것 보다 편한 것을 느꼈다. 간단하게 db에 접근이 가능했고 코드도 확실히 줄어들었음을 느꼈다. 트랜잭션에 대한 개념도 한번 다지기 좋았던 것같다. 영속성이란 부분에 대한 개념을 다시 생각해볼 수 있는 기회였다. 1주차에 비해 2주차는 조금 느슨해진감이 없지않아 있었다.... 3주차에는 불태울것이다! 2주차에 어려웠던 내용도 복습을 해야겠다.

백엔드백엔드워밍업스터디

lar

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

1. 2주차 학습한 내용 [6일차 - 스프링 컨테이너의 의미와 사용 방법]1. UserController와 스프링 컨테이너스프링 빈이란?서버가 시작되면, 스프링 서버 내부에 컨테이너를 생성한다.컨테이너 안에 클래스가 들어간다.이때 다양한 정보도 들어있고, 인스턴스화도 이루어진다.들어간 클래스를 스프링 빈이라고 한다.JdbcTemplate 의존성 주입이 되어있다.JdbcTemplate을 스프링 빈으로 등록해주는 설정 코드가 있다.dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' } 서버 시작 시스프링 컨테이너 시작 -> 기본적으로 많은 스프링 빈 등록 -> 우리가 설정한 스프링 빈(UserController) 등록 -> 의존성 자동으로 설정스프링 빈으로 등록각 클래스가 JdbcTemplate을 가져오기 위해 스프링 빈으로 등록한다.어노테이션 추가한다.2. 스프링 컨테이너를 왜 사용할까?Interface 활용BookController는 BookService을 사용한다.BookService는 BookRepository 인터페이스 사용한다.또 다른 Repository(Repository1, Repository2)가 있다. BookService 코드를 변경하지 않고 BookRepository을 변경할 수 있는 방법이 스프링 컨테이너이다.스프링 컨테이너 사용시컨테이너가 Repository1 또는 Repository2 중 하나를 선택한 후 BookService를 생성해준다. 이러한 방식을 제어의 역전(IoC, Inversion of Control)이라고 한다.컨테이너가 BookService를 생성할 때, Repository1와 Repository2 중 하나를 선택해서 넣어준다. 이러한 과정을 의존성 주입(DI, Dependency Injection)이라도 한다.우선권을 부여하는 어노테이션 활용@Primary : 우선권을 결정하는 어노테이션을 의미한다.@Primary 어노테이션을 사용 시 Service 코드 변경없이 해당 Repository를 사용할 수 있다.3. 스프링 컨테이너를 다루는 방법빈을 등록하는 방법@Configuration클래스에 붙이는 어노테이션이다.@Bean을 사용할 때 함께 사용해야 한다.@Bean메서드에 붙이는 어노테이션이다.메서드에서 반환하는 객체를 스프링 빈에 등록한다.@Configuration과 @Bean 사용하는 경우외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때 사용한다.@Component주어진 클래스를 컴포넌트로 간주한다.이 클래스들은 스프링 서버를 시작할때 자동으로 감지된다.언제 @Component가 사용될까?Controller, Service, Repository가 아닌 추가적인 클래스를 스프링 빈으로 등록할 때 종종 사용된다.스프링 빈 주입 받는 방법생성자 사용(@Autowired 생략 가능) -> 가장 권장한다.@Setter와 @Autowired 사용 -> 누군가 Setter를 사용하면 오작동할 수 있다.필드에 직접 @Autowired 사용 -> 테스트를 어렵게 만드는 요인이다.@Qualifer("value") 사용여러가지 후보군이 있을 때 특정 하나를 가져오고 싶을때 사용한다.스프링 빈을 사용하는 쪽과 스프링 빈을 등록하는 쪽 모두 사용할 수 있다.적어준 값이 동일한 것끼리 연결된다.@Primary와 @Qualifer 함께 사용할 경우우선권을 결정해주는 @Primary와 @Qualifer을 함께 사용했을 때 누가 사용될까?@Qualifer을 사용한다.> 스프링 빈을 사용하는 쪽에서 특정 빈을 지정해준 것의 우선순위를 더 높게 간주한다.  [7일차 - Spring Data JPA를 사용한 데이터베이스 조작]1. 문자열 SQL을 직접 사용하면 좋지 않은 이유문자열을 작성하기 때문에 실수할 수 있고, 실수를 인지하는 시점이 느리다. 컴파일 시점에 발견되지 않고 런타임 시점에 발견된다.특정 데이터베이스에 종속적이게 된다. DB의 종류마다 문법이 조금씩 다르다.반복 작업이 많아진다. 테이블 하나 만들 때마다 CRUD 쿼리가 항상 필요하다.데이터베이스의 테이블과 객체는 패러다임이 다르다.2. JPA(Java persistence API)JPA란?자바 진영의 ORM(Object-Relational Mapping) 기술 표준을 의미한다.객체와 관계형 DB의 테이블을 매핑하여 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙을 의미한다.Hibernate규칙을 구현한 구현체를 Hibernate(하이버네이트)라고 한다.Hibernate는 내부적으로 JDBC를 사용한다.3. 유저 테이블에 대응되는 Entity Class 만들기 - Java 객체와 MySQL Table 매핑하기JPA 어노테이션@Entity : 저장되고, 관리되어야 하는 데이터를 의미한다.@Id : 해당 필드를 primary key로 간주한다.@GeneratedValue : primary key는 자동 생성되는 값이다.@Column(생략 가능) : 객체의 필드와 Table의 필드를 매핑한다.기본 생성자 추가기본 생성자도 추가해야 한다.@Entity public class User { protected User() { } } application.yml - JPA 설정 옵션 추가spring: jpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql: true dialect: org.hibernate.dialect.MySQL8Dialect 4. Spring Data JPA 사용 - 자동으로 쿼리 전송하기SQL을 작성하지 않고 유저 생성/조회/업데이트 기능 리팩토링하기유저 생성 기능 리팩토링하기Interface UserRepository인터페이스 UserRepository 생성 후 JpaRepository를 상속 받는다.UserServicesave() 메서드에 객체를 삽입하면 Insert SQL이 자동으로 전송된다.저장이 된 후 User에는 id가 들어간다. 유저 조회 기능 리팩토링하기UserResponse 생성자 추가코드를 더 깔끔하게 만들기 위해 생성자를 추가한다.UserServicefindAll 메서드를 사용하면 모두 유저의 데이터를 조회하는 SQL이 전송된다.그 결과, List가 반환되고, UserResponse로 변경해서 전달한다.유저 수정 기능 리팩토링하기UserfindById를 사용하면 id를 기준으로 1개의 데이터를 가져온다.Optional의 orElseThrow를 사용해 User가 없을 경우 예외처리한다.객체를 수정해주고, save 메서드를 호출한다.자동으로 UPDATE SQL이 전송된다.SQL을 작성하지 않아도 동작 하는 이유Spring Data JPA를 통해 SQL을 작성하지 않아도 동작한다.Spring Data JPA : 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리이다.5. Spring Data JPA 사용 - 다양한 쿼리 작성하기By 앞에 들어갈 수 있는 구절 정리find : 데이터 1건을 가져온다. 반환 타입은 객체, Optional<타입>이다.findAll : 쿼리의 결과물이 N개인 경우 사용한다. List<타입>을 반환한다.exists : 쿼리 결과가 존재하는지 확인한다. 반환 타입은 boolean이다.count : SQL의 결과 개수를 센다. 반환 타입은 long이다.각 구절은 and 또는 or로 조합할 수 있다.By 뒤에 들어갈 수 있는 구절 정리GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이에StartsWith : ~로 시작하는EndsWith : ~로 끝나는 [8일차 - 트랜잭션과 영속성 컨텍스트]1. 트랜잭션(Transaction)트랜잭션이란?쪼갤 수 없는 업무의 최소 단위를 의미한다. (모두 성공하거나 실패하는 경우)트랜잭션 동작 명령어트랜잭션 시작하기 : start transaction;트랜잭션 정상 종료하기 (SQL 반영) : commit;트랜잭션 실패 처리하기 (SQL 미반영) : rollback;2. 트랜잭션 적용과 영속성 컨텍스트트랜잭션 적용하기@Transactional 어노테이션을 사용한다.@Service public class UserServiceV2 { //조회 - readOnly 옵션 사용 가능(SELECT 쿼리) @Transactional(readOnly = true) public List<UserResponse> getUsers() { return userRepository.findAll().stream().map(UserResponse::new).collect(Collectors.toList()); } } 메서드가 시작될때 Transactional 시작된다.메서드 로직이 정상적으로 성공하면 commit되고, 문제가 생길 경우 rollback된다.트랜잭션을 사용 하는 이유트랜잭션이 없으면 코드가 한줄 한줄 반영된다.오류가 생겨도 다른 코드는 반영된다.주의사항org.springframework.transaction.annotation.Transactional을 붙여야 한다.IOException과 같은 Checked Exception은 롤백이 일어나지 않는다.영속성 컨텍스트이란?테이블과 매핑된 Entity 객체를 관리 및 보관하는 역할을 의미한다.트랜잭션과 영속성 컨텍스트의 관계트랜잭션 사용 -> 영속성 컨텍스트 시작트랜잭션 종료 -> 영속성 컨텍스트 종료영속성 컨텍스트의 특징변경 감지(Dirty Check) : 영속성 컨텍스트 안에서 확인된 Entity는 명시적으로 save하지 않아도 변경을 감지해서 자동으로 저장한다.쓰기 지연 : DB의 INSERT/UPDATE/DELETE SQL을 바로 전송하지 않고, 트랜잭션이 commit될 때 SQL을 모아서 한번만 전송한다.1차 캐싱 : ID를 기준으로 Entity를 기억한다. 최초 1회만 쿼리가 전송되고, 나머지는 보관하고 있는 데이터를 활용한다. 이렇게 반환된 객체는 동일하다. [9일차 - 조금 더 복잡한 기능을 API로 구성하기]1. 책 생성/대출/반납 기능 API 개발하기book 테이블 및 객체 생성, DTO, Repository, Service, Controller 구현대출 기록에 대한 Table 추가, 대출 기록 Table에 대한 객체 생성, DTO, Repository, Service, Controller 구현생성한 테이블로 구현이 충분하지만, DTO를 새로 만드는 게 낫다.다른 기능들 중 한 기능에 변화가 생길 경우 더 유연하게 side-effect가 없이 대처가 가능하기 때문이다.2. 미션네 번째 과제(4일차)다섯 번째 과제(5일차)3. 회고1주차보다 학습한 내용이 많아진 만큼 정리를 잘하고 공부를 많이 해야겠다고 느꼈다. 금요일에 해주신 특강도 너무 도움이 되었다. 내용이 나한테는 아직 어려웠지만 다른 사람들의 코드를 보면서 나도 좀 더 고민해보고 코드를 짜고 과제도 해봐야겠다.

백엔드발자국회고워밍업2주차

lar 10일 전
최강현

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

Day 7. 스프링 컨테이너의 의미와 사용 방법 이 날 강의를 통해서 드디어 베일에 쌓인 의존성을 공부하는 기회가 되었다.의존성이란다음 코드를 보면 UserController의 생성자는 JdbcTemplate이라는 클래스를 필요로 하고 있다. @RestController public class UserController { private final UserService userService; public UserController(JdbcTemplate jdbcTemplate) { this.userService = new UserService(jdbcTemplate); } }이것을 어려운 말로 '의존한다'라고 한다. 스프링 빈이란스프링부트 서버를 시작할 때 자동으로 해주는 것 중 하나가 거대한 컨테이너(실제 컨테이너를 떠올리면 된다)를 만드는 것이다.이 컨테이너 안에는 클래스가 들어가게 되는데 이렇게 들어간 클래스를 스프링 빈이라고 부른다.클래스가 들어갈 때는 이 빈을 식별 할 수 있는 이름 및 타입과 다양한 정보가 들어가고 인스턴스화도 이루어진다. Day8. Spring Data JPA를 사용한 데이터베이스 조작 8일차 강의를 통해 SQL을 통해서 접근했던 DB를 자바 객체를 DB와 매핑 시켜서 사용하는 JPA를 학습하였다. JPA란객체와 관계형 데이터베이스의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙JPA는 API이기 때문에 말 그대로 규칙이고 (자바의 인터페이스와 같다) 이 규칙을 누군가는 실제로 구현을 해야한다그 구현체가 바로 Hibernate이다. Day9. 트랜잭션과 영속성 컨텍스트 9일차 강의는 서비스 계층의 남은 중요한 역할인 트랜잭션에 대해 배웠다.트랜잭션이란 쪼갤 수 없는 업무의 최소단위이다.실제로 예를 들면 인터넷 결제를 진행할 때 1. 주문 기록을 저장하고 2. 포인트를 적립해주고 3. 구매 기록을 저장해준다이 3가지의 과정을 쪼갤 수 없는 하나의 최소단위로 묶고 하나가 실패하면 다른 모든 과정도 실패해버리게 만드는게 트랜잭션이다. Start transaction -> 성공하면 COMMIT -> 실패하면 ROLLBACK 또한 영속성 컨텍스트에 관해서도 학습했는데 조금 어려워서 꼭 기억해야하는것스프링에서는 트랜잭션을 사용하면 영속성 컨택스트가 생기고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다. 영속성 컨텍스트의 특별한 능력변경 감지 : 영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save를 해주지 않더라도 알아서 변경을 감지하여 저장할 수 있게 해준다.쓰기 지연 : 영속성 컨텍스트에 의해 트랜잭션이 Commit 되는 시점에 SQL을 모아서 한 번만 날리게 된다.1차 캐싱 : ID를 기준으로 Entity를 기억하는 기능이다. 조금 더 복잡한 기능을 API로 구성하기 10일차는 앞서 배운 것들로 JPA를 이용해서 책 생성, 대출, 반납 API를 개발해보았다. 과제 과제 4일차https://devnter.tistory.com/entry/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-1%EA%B8%B0BE-4%EC%9D%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9CqueryForObject() 에 대한 학습이 필요. 과제 5일차https://devnter.tistory.com/entry/%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%9B%8C%EB%B0%8D%EC%97%85-%ED%81%B4%EB%9F%BD-1%EA%B8%B0BE-5%EC%9D%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C주어진 코드를 클린코드로 리팩토링하는 과제를 수행하였다.클래스를 나누기에는 너무 작은 기능이라 생각해서 읽기 좋은 코드로만 수정한다는 생각으로 수정했는데나중에 금요일에 코치님이 따로 열어주신 특강에서 테스트를 하기 위해 역할 별로 클래스를 쪼개고 테스트를 하는 강의를 진행 해 주셔서 새로운 내용에 대해 학습하는 기회가 되었다리팩토링 전에는 테스트를 작성하자! (Junit5, assertj)given / when / then 패턴전략 패턴 (= NumberGenerator의 인터페이스화 + 구현체 갈아끼우기)일급컬렉션MVC 패턴출처 https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EC%98%AC%EC%9D%B8%EC%9B%90# 아쉬운점좀 더 추가적인 학습(모르는것에 대한 학습)을 하려고 했는데 나태해져서 진행하지 못했다. 다음 마지막 주차는 좀 더 내가 모르는 내용에 대해 민감하게 반응해서 학습 하거나 학습 하지 못한다면 따로 블로그나 노션에 기록해서 추후 공부할 수 있도록 하겠다. 

백엔드인프런워밍업클럽자바스프링javaspring

준섭

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

학습 내용스프링 컨테이너의 의미와 사용 방법스프링 빈스프링은 스프링 서버 내부에 거대한 컨테이너를 만들고이 컨테이너 안에는 여러 클래스가 들어간다.이때 다양한 정보도 함께 들어 있고 인스턴스화도 이루어짐JdbcTemplate도 스프링 빈에 등록되어 있어서 의존성을 등록해준다.서버가 시작되면 일어나는 일스프링 컨테이너 시작기본적인 스프링 빈 등록우리가 설정한 스프링 빈 등록이때 필요한 의존성 자동 설정UserRepository는 JdbcTemplate을 가져오지 못한이유UserRepository가 스프링 빈이어야 하는데 UserRepository는 스프링 빈이 아니기 때문에UserRepository를 스프링 빈으로 등록하면 가져올 수 있다.@Service와 @Repository 어노테이션을 사용해 스프링 빈으로 등록스프링 컨테이너를 다루는 방법스프링 빈 등록하는 방법@Configuration클래스에 붙이는 어노테이션@Bean을 사용할 때 함께 사용해 주어야 함@Bean메소드에 붙이는 어노테이션메소드에 반환되는 객체를 스프링 빈에 등록@Component주어진 클래스를 컴포넌트로 간주이 클래스들은 스프링 서버가 뜰 때 자동으로 감지@Component 덕분에 우리가 사용했던 어노테이션이 자동감지Controller, Service, Repository가 모두 아니고 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용스프링 빈 주입받는 방법생성자를 이용해 주입받는 방식@Autowired 생략 가능Setter와 @Autowired 사용누군가 setter를 사용하면 오작동할 가능성 있음필드에 직접 @Autowired 사용테스트를 어렵게 만드는 요인@Qualifier@Primary vs @Qualifier사용하는 쪽이 직접 적은 @Qualifier이 우선적Spring Data JPA를 사용한 데이터베이스 조작SQL을 직접 작성하면 아쉬운 점문자열을 작성하기 때문에 실수할 수 있고 실수를 인지하는 시점이 느림컴파일 시점에 발견되지 않고 런타임 시점에 발견됨특정 데이터베이스에 종속적이게 됨반복 작업이 많아지며 테이블을 하나 만들 때마다 CRUD 쿼리가 항상 필요데이터베이스의 테이블과 객체는 패러다임이 다름JPA(Java Persistence API)객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙Hibernate: 말로 되어 있는 규칙을 코드로 구현한 것Hibernate(구현체)이 JPA를 구현내부적으로 JDBC 사용유저 테이블에 대응되는 Entity Class 만들기Java 객체 <-> MySQL Table 매핑JPA 어노테이션@Entity: Spring이 User 객체와 user 테이블을 같은 것으로 바라봄@Id: 이 필드를 primary key로 간주@GeneratedValue: primary key는 자동 생성되는 값임@Column: 객체의 필드와 Table 필드 매핑JPA를 사용하기 위해서는 기본 생성자가 반드시 필요JPA 추가 설정ddl_auto: 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지 (create, create-drop, update, validate, none)format_sql: SQL을 보여 줄 때 예쁘게 포맷팅 할 것인가show_sql: JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여 줄 것인가dialect: 조금씩 다른 SQL을 수정해 줌Spring Data JPA를 이용해 자동으로 쿼리 날리기JPA 기능save: 주어지는 객체를 저장하거나 업데이트findAll: 주어지는 객체가 매핑된 테이블의 모든 데이터 가져옴findById: id를 기준으로 특정한 1개의 데이터 가져옴Spring Data JPA복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리Spring Data JPA를 이용해 다양한 쿼리 작성하기By 앞에 들어갈 수 있는 구절 정리find: 1건을 가져옴, 반환 타입은 객체가 될 수도 있고 Optional<타입>이 될 수도 있음findAll: 쿼리의 결과물이 N개인 경우 사용, List<타입> 반환exists: 쿼리 결과가 존재하는지 확인, 반환 타입은 booleanBy 뒤에 들어갈 수 있는 기능 정리And / Or 조합 가능GreaterThan: 초과GreaterThanEqual: 이상LessThan: 미만LessThanEqual: 이하Between: 사이에StartsWith: ~로 시작하는EndsWith: ~로 끝나는트랜잭션과 영속성 컨텍스트트랜잭션쪼갤 수 없는 업무의 최소 단위트랜잭션 시작: start transaction;트랜잭션 정상 종료: commit; (SQL 반영)트랜잭션 실패 처리: rollback; (SQL 미반영)영속성: 모든 SQL을 성공시키거나 하나라도 실패하면 모두 실패시키는 속성트랜잭션 적용과 영속성 컨텍스트@Transactional스프링에서 트랜잭션 적용할 때 사용IOException과 같은 Checked Exception은 롤백이 일어나지 않음영속성 컨텍스트테이블과 매핑된 Entity 객체를 관리 및 보관하는 역할변경 감지(Dirty Check)영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도 변경을 감지해 자동으로 저장쓰기 지연DB의 INSERT / UPDATE / DELETE 문을 바로 날리는 것이 아니라 트랜잭션이 commit 될 때 한번에 날림1차 캐싱ID를 기준으로 Entity를 기억캐싱된 객체는 동일한 객체

백엔드

yuri

[인프런 워밍업 스터디 1기 디자인] 2주차 발자국 및 회고

[인프런 워밍업 스터디 1기 디자인] 2주 차 발자국평일에 틈틈이 강의를 수강하고자 노력했지만,결국 주말에 몰아서 강의를 듣게 되어 과제가 많이 밀렸고 보너스 미션도 수행하지 못했다. 😭아쉬움이 남는 한 주였고 수행하지 못한 미션은 이후에도 틈틈이 작업해 봐야겠다.2주차 강의 수강을 통해 배운 것강의를 보고 다양한 컴포넌트를 따라 만들어보면서 프로퍼티 사용법에 익숙해질 수 있었다.defalut, hover, press 등 케이스별로 컴포넌트를 만들면서 입력 플로우에 더 친숙해질 수 있었다. 특강에서 배운 팁을 빌런아이콘에 적용하며 포기했던 어려움을 해결할 수 있었다. 🙏 3주 차에서 보완하고 싶은 점강의에서 배운 것을 바로 메모하는 습관들일 것.2주 차 강의에서 배운 것을 응용할 수 있도록 보너스 미션을 완수할 것.다른 서비스의 디자인 가이드를 서치하고 적용할 수 있는 부분을 찾아볼 것.2주차는 컴포넌트를 만들면서 피그마 툴에 친숙해진 것 같다.확실히 강의를 긴 시간 수강하니 집중도도 올라가고 따라 만들어보는 재미도 있었다.아직 시간에 쫓겨서 하고 있지만, 휴일에 밀린 강의를 수강해 두어야 할 것 같다.3주차도 화이팅..!!!✊✊

UX/UI워밍업스터디UIUX디자인디자인시스템

장종원

[인프런 워밍업 클럽 1기/BE] 두번째 발자국

2주차 발자국을 찍으며..생각보다 시간을 잘 활용하지 못한 것 같다. 이 강의를 들으며, 잊었던 자바8 이후 문법들에 대해서도 익숙해지도록 공부하려고 마음 먹었으나 급한 일이 겹치고 오늘만은 늦더라도 강의 듣고 공부 더 하고 자야지 했던 다짐이 조금 무너졌었다.다음주는 꼭 강의와 과제는 일찍 챙겨 듣고 추가 공부를 더 해서 많이 남겨보는 게 목표이다.아 그리고 월요일(한 주의 시작)에 그 주의 계획을 세워보고 그대로 살아보기 또한 목표이다.섹션4. 생애 최초 JPA 사용하기Spring Data JPA를 사용한 데이터베이스 조작. 23~26강SQL 사람이 직접 작성하면 아쉬운 점에러가 컴파일 시점(서버를 실행하는 그 순간. .java → .class로 변환시켜서 jvm에서 동작시키는 과정)에 발견되지 않고, 런타임 시점(실제 서버가 동작하고 나서)에 발견된다.특정 데이터베이스에 종속적이게 된다반복 작업이 많아진다. 테이블 하나 만들 때마다 CRUD 쿼리가 필요.DB의 테이블과 객체는 패러다임이 다르다.객체에선 교실과 학생은 서로를 가리킬 수 있다.DB에선 학생만 교실을 가리킨다.상속관계는 DB 테이블로 표현하기 어렵다요즘은 어플리케이션을 객체지향적인 설계를 지향. DB 테이블을 직접 쓰기가 애매함그래서 등장하게 된 것이 JPA(Java Persistence API)자바 진영의 ORM(Object-Relational Mapping)영속성 : 서버가 재시작되어도 데이터는 영구적으로 저장되는 속성객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 JAVA 진영의 규칙말로 되어 있는 규칙을 코드로 구현 → 그 코드가 HIBERNATE(JPA를 구현하는 구현체)Hibernate는 내부적으로 JDBC를 사용한다. Java 객체와 MySQL 테이블 매핑@Entity : 스프링이 User객체와 user 테이블을 같은 것으로 바라본다. Entity의 의미는 저장되고, 관리되어야 하는 데이터객체엔 없었던 id 필드를 추가해준다. @Id : 이 필드를 primary key로 간주한다. @GeneratedValue(strategy = GenerationType.*IDENTITY*) (MySQL의 Auto_Increment와 동일. primary key는 자동생성되는 값이다.) ※ DB마다 자동 생성 전략이 다르다고 함JPA를 사용하려면 Entity 객체에는 기본생성자(매개변수가 없는)가 필수임. protected로 해도 됨@Column : 객체의 필드와 Table의 필드를 매핑한다. 여러 DB의 설정을 연결함ex) @Column(nullable = false, length = 20, name = "name")필드의 이름도 name이고 DB의 컬럼 이름도 name이므로 생략가능. 필드의 이름이 다를 때 연결시켜줄 때 사용하기도 함필드가 컬럼과 동일할 경우 @Column 어노테이션을 생략할 수 있다.JPA를 사용하려면 application.yml 파일에 아래와 같이 추가가 필요하다ddl-auto : 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지DB와 객체가 서로 다르게 생겼으면 검증할 수도 있고, 무시할 수도 있고, 테이블을 바꿀 수도 있음create : 기존 테이블이 있다면 삭제 후 다시 생성(들어있는 데이터 삭제됨)create-drop : 스프링이 종료될 때 테이블을 모두 제거(들어있는 데이터 삭제됨)update : 객체와 테이블이 다른 부분만 변경validate : 객체와 테이블이 동일한지 확인(실패하면 서버를 종료시킴)none : 별다른 조치를 하지 않는다.show_sql : JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여줄 것인가format_sql : SQL을 보여줄 때 예쁘게 포맷팅할 것인가dialect(방언,사투리) : 이 옵션으로 DB를 특정하면 조금씩 다른 SQL을 수정해준다.public interface UserRepository extends JpaRepository<User, Long> { }domain에 user 옆에 UserRepository 인터페이스를 하나 만든다. 이후 JpaRepository를 상속받고 <> 안에 Entity 객체, User의 id 타입을 적어주면 된다이 인터페이스는 @Repository를 붙이지 않아도 JpaRepository를 상속받는 것만으로 Spring bean으로 관리된다.저 인터페이스의 save 메소드에 객체를 넣어주면 INSERT SQL이 자동으로 날아간다.save되고 난 후의 User에는 id가 들어있다.해당 인터페이스의 findAll 메소드 를 사용하면 모든 데이터를 가져온다.(select * from user)위와 같이 작성해주면(객체를 업데이트 해주고나서 save 메소드를 호출) 마지막에 save할 때 변경사항이 있으면 자동으로 바뀐 객체정보로 update문이 날아가게 됨SQL을 작성하지 않아도 동작하는 이유Spring Data JPA : 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리SimpleJpaRepository : 어려운 JPA코드를 감싼 라이브러리(사용자가 쉽게 쓸 수 있게)다양한 쿼리를 Spring Data JPA로 변경해보자userRepository.delete(user); 로 데이터를 DB에서 제거하게 됨기본 제공되지 않는 메서드 만드는 법함수 이름이 중요함. 알아서 SQL이 조립됨.find라고 작성하면, 1개의 데이터만 가져온다.By 뒤에 붙는 필드 이름으로 SELECT 쿼리의 WHERE문이 작성된다.By 뒤에 올 수 있는 구절트랜잭션과 영속성 컨텍스트(27~29강)저번시간까지는 Repository 계층이 하는 일 처리, 이제는 Service 계층이 해야하는 추가적인 역할인 transaction에 대해 알아보자트랜잭션쪼갤 수 없는 업무의 최소 단위모든 SQL을 성공시키거나, 하나라도 실패하면 모두 실패시키자트랜잭션 명령어시작 : start transaction;정상 종료 : commit;실패처리 : rollback;※ 트랜잭션을 시작했을 때 commit 전 데이터를 다른 접속에서도 보이게 할 수는 있다(옵션조절로) 하지만 대부분 그러지 않는 기본값을 사용함트랜 잭션 적용 방법@Transactional 어노테이션 사용함수에 붙이면 함수가 시작될 때 트랜잭션이 시작되고 함수가 종료될 때 commit(정상종료시)/rollback(에러발생시)을 해주도록 적용SELECT 쿼리만 사용한다면, (readOnly = true) 옵션을 붙일 수 있다.저장, 업데이트, 삭제 등 데이터 변경을 위한 불필요한 기능이 빠져서 약간의 성능적 이점이 있음Service 계층의 중요한 역할 : 비즈니스 로직을 가지고 있는 것, 여러 SQL에 접속해야할 경우 트랜잭션 관리※ 주의사항 : IOException 같은 Checked Exception은 롤백이 일어나지 않는다. (서비스계층에서 날 일은 거의 없긴 하다)영속성 컨텍스트 : 테이블과 매핑된 Entity 객체를 관리/보관하는 역할스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.영속성 컨텍스트의 특수 능력 4가지변경 감지(Dirty Check) : 영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도, 변경을 감지해 자동으로 저장된다. ⇒ updateUser 함수에서 userRepository.save를 지워도 자동으로 저장됨쓰기 지연 : 영속성 컨텍스트 안에서 DB INSERT/UPDATE/DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번만 날린다.(통신횟수 줄고 비용이 줄어듬)1차 캐싱 : ID를 기준으로 Entity를 기억한다(통신횟수 줄고 비용이 줄어듬), 이렇게 캐싱된 객체는 완전히 동일하다(자바의 같은 객체. 주소가 같은 객체와 같은 의미)나머지 하나는 다음 섹션에서….!과제4. 추가적인 API를 만들어 보며 API 개발에 익숙해지기https://slime-feels-660.notion.site/60da5bdb68b9456cad02df9659fff823?pvs=44-1은 강의 때 배운대로 3단계의 layer로 분리하여 코드를 작성하였고 자바의 LocalDate를 MySQL의 date 형식과 맞춰 작업하면 되겠구나 생각한 것 외에는 간단했던 것 같다.4-2는 테이블을 변경하는 DDL을 검색해서 풀었다. 잠시 까먹었던 문법을 상기시킬 수 있었다.4-3은 문제가 제시하는 형태로 API 결과값을 만들어야 해서 SQL을 두 번 보내는 방식으로 할 지, SQL문 자체를 group by / sum 을 사용해서 후처리를 할 지 고민하다가 두번째 방법을 쓰고 최대한 배운대로 작업해보고자 노력해서 조금 시간이 더 걸렸다. 잊었던 SQL들을 공부한 느낌이라 역시 계속 한 번씩이라도 다시 봐야겠다고 생각했다.<추가> 자바에서 정수를 다루는 가장 대표적인 두 가지 방법은 int 와 long 입니다. 이 두 가지 방법 중 위 API에서 long 을 사용한 이유는 무엇일까요?나도 비슷하게 답변을 하였지만, 실제 면접에서 답변하듯이 깔끔한 대답은 잘 못 한 것 같다. 값이 커질 수 있다는 말만 하였고 오버플로우가 발생할 수 있다는 식의 대답은 하지 못한 것 같아 조금 더 이런 질문에면접에서의 원하는 깔끔한 대답을 할 수 있도록 준비해야 겠다고 생각이 들었다.+) 4번 과제.. 날짜를 착각해서 제출이 조금 늦어졌지만 새벽에 끝까지 집중해서 과제를 마무리하고 제출 후 잠들어 그나마 마음의 짐이 덜어진 것 같았고, 꼼꼼히 과제 체크 및 강의 듣는 것을 다시 한 번 다짐했다..과제5. 주어진 코드를 클린 코드로 고쳐보기https://slime-feels-660.notion.site/ba498281917f439b8f230cb8f87ac29b?pvs=4인터넷 검색을 해보며 클린 코드에 대해 다시 한 번 생각해보게 되었고, 모듈화 하는 것을 나는 솔직히 코딩작업 시간을 늘리고 굳이 이렇게까지 나눠야 하나 라는 생각이 더 컸었다. 그런데 생각을 해보니, 나만 볼 때야 다 알아볼 수 있으니까 그런거지라는 생각이 들었고 코드를 깔끔하게 작업한다면 함께 일하는 사람들이 볼 때 훨씬 편하겠다는 생각을 하게 되었다.여태까지 나와 함께 프로젝트를 했던 사람들은 그럼 힘들었을까? 라는 생각을 해보며 최대한 모듈화, 함수명 네이밍, 주석 등을 신경쓰며 과제를 진행하였다.마무리2주차가 벌써 지나갔다. 시간이 참 빠른 것 같다. 아직 공부하고 싶은 것도 많고 해야할 것도 많다고 생각한다.절대 나태해지지 말고 강의 꾸준히, 쉬는 시간 적당히, 적절한 운동으로 건강까지..모두 챙겨서 3주차엔 행복하게 발자국을 남길 수 있길 바래본다.

백엔드백엔드워밍업클럽발자국

노은빈

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

저번주에 호기롭게 다짐했던 이번주에는 공부를 미리 다해보자!라고 했는데오히려 저번주보다 공부를 덜 했다...분명 할 시간이 많을거라 생각했는데 이것저것 해보던 취준과 여행의 여파로 너무 피곤하기만 해서 뭘 제대로 해보지도 못하고 한 주가 지나갔다.열심히 해보려던 과제도 결국 두 개 중 하나만 제출하고 너무 허무한 한 주이다.그래서 그런지 더욱 머리에 내용이 안 남지만,, 낼부터는 진짜 열심히 해야겠다고 다시 한 번 다짐한다!! 이번주에는 나름의 복습으로 저번주에 공부하면서 정리했던 내용들을 다시 읽어보았다.사실 자바도 익숙하지 않아서 스프링은 더 안 낯선 언어인데 그래도 한 번 쓰면서 공부해봤다고 조금 눈에 익은 것 같다.섹션3 밖에 다 하지 못 했지만 다음주에는 얼른 진도에 맞춰 끝내놔야겠다. Section3역할의 분리와 스프링 컨테이너라는 제목이다.역할의 분리가 얼마나 중요할까?에 대해서 생각하면서 코드를 짜본적은 없던 것 같다. 아무래도 급하게 프로젝트만 하니 아 그냥 분리하는게 좋구나 해서 분리했던 기억이 난다. Layered Architecture3단 분리를 해야하는 이유?!💡 Controller에서 모든 기능을 구현하면 왜 안 될까?=> 함수는 최대한 작게 만들고 한 가지 일만 수행하는 것이 좋다고 설명해주셨다. 클래스는 작아야 하며 하나의 책임만을 가져야 한다.예를 들어, 만약 controller 함수 1개가 3000줄이 넘으면?그 함수를 동시에 여러 명이 수정할 수 없음그 함수를 읽고, 이해하는 것이 너무 어려움그 함수의 어느 부분을 수정하더라도 함수 전체에 영향을 미칠 수 있기 때문에 함부로 건들 수 없게 됨너무 큰 기능이기 때문에 테스트도 힘듦종합적으로 유지보수성이 매우 떨어짐결국 이런 이유로 Controller에 모든 기능을 넣기 보다는 Service와 Repository에 나누는 게 좋다!!여태 작성했던 코드는API의 진입 지점으로써 HTTP Body를 객체로 반환현재 유저가 있는지 없는지 확인하고 예외 처리함SQL을 사용해 실제 Database와의 통신을 담당를 Controller에서 모두 하고 있었는데 1번은 Controller, 2번은 Service, 3번은 Repoitory로 나누는 작업을 한다. 3가지 역할을 나누는 것을 Layered Architecture 라고 하는데Controller ← Service ← Repository (DTO는 계층 간의 정보 전달 역할) 로 나눠진다. 이 때 기능만 나눠서는 안 된다. 우리는 JdbcTemplate 를 파라미터로 계속 받고 있어 오류가 생긴다.해결 방법은 UserRepository 도 Usercontroller 처럼 JdbcTemplate 를 변수로 받으면 된다. 그럼 또 다음과 같은 의문점이 생긴다💡Controller에 있는 JdbcTemplate 는 어디서 온 걸까? → Repository에서 바로 JdbcTemplate를 가져올 수는 없을까?답은 @RestController이다.@RestController어노테이션을 사용하면 UserController 클래스를 API의 진입 지점으로 만들 뿐 아니라 UserController 클래스를 스프링 빈으로 등록시키는 역할을 해준다. 스프링빈이란?서버가 시작되면, 스프링 서버 내부에 거대한 컨테이너를 만들고, 컨테이너 안에는 클래스가 들어가게 됨⇒ 이 때 다양한 정보도 함께 들어있고, 인스턴스화도 이루어짐즉, 이때 스프링 컨테이너 안으로 들어간 클래스를 스프링 빈이라고 한다.JdbcTemplate도 이미 스프링 빈으로 등록되어 있음JdbcTemplate은 우리가 build.gradle에서 설정한 dependency(의존성)에서 등록해주고 있었다! 스프링 컨테이너는 필요한 클래스를 연결해준다서버가 시작되면 다음과 같은 일이 일어나는데스프링 컨테이너(클래스 저장소)가 시작기본적으로 많은 스프링 빈들이 등록됨(JdbcTemplate)우리가 설정해준 스프링 빈 등록(UserController)이때 필요한 의존성이 자동으로 설정됨💡 그렇다면 왜 UserRepository는 JdbcTemplate을 가져오지 못할까?⇒ JdbcTemplate 을 바로 가져오려면 스프링 빈이어야 하는데 UserRepository는 스프링 빈이 아니다⇒ UserRepository를 스프링 빈으로 등록해보자!!여기서 Repostitory에는 @Repostitory Service에는 @Service 를 각각 등록해주면 스프링 빈으로 등록된 것이다.  💡 좋아진 것 같긴 한데 왜 스프링 컨테이너를 사용하지? 그냥 new 연산자 쓰면 되는거 아닝가?? 라는 의문이 들 것이다.책을 저장하는 api를 만든다고 가정해보자.그럼 Controller, Service, Repository 이렇게 구성하고 만들 것이다.이 때 db를 메모리에만 저장하는 코드로 구성했다가, MySql과 같은 db에 저장한다고 하면repository가 수정될 때마다 새로 new BookMySqlRepository();  이런 식으로 계속 추가해야 한다.자동으로 연결해주기 위해 스프링 컨테이너를 활용한다! 스프링 컨테이너란 컨테이너가 BookService를 대신 인스턴스화 하고 그 때 알아서 BookRepository를 결정해주는 건데 BookService와 BookRepository가 모두 스프링 빈이라면 스프링 컨테이너는 BookService를 알아서 인스턴스화 하고BookRepository를 결정해줄 것이다. BookRepositorypackage com.group.libraryapp.repository.book; import org.springframework.stereotype.Repository; @Repository public class BookMySqlRepository implements BookRepository{ @Override public void saveBook() { } } BookControllerpublic class BookController { // 2. BookService 연결 => 스프링 빈이 아닌 경우 private final BookService bookService; // instance화 할 필요 X public BookController(BookService bookService) { this.bookService = bookService; } // 생성자 추가(constructor)이렇게 하면 알아서 어떤 레포지토리를 불러올지 결정한다.이때 레포지토리가 너무 많다면 @Primary 를 사용해 어떤 레포지토리를 우선순위에 둘지 레포지토리에 직접 설정해준다. 빈을 사용하는 방법@Configuration와 @Bean 이 있는데 둘은 항상 같이 사용해야 한다.@Configurationclass에 붙이는 어노테이션@Bean 을 사용할 때 함께 사용해 주어야 함@Beanmethod에 붙이는 어노테이션메소드에 반환되는 개체를 스프링 빈에 등록@Configuration 이라는 어노테이션은 보통 config 라는 패키지에 만든다.@Component주어진 클래스를 컴포넌트로 간주컴포넌트로 간주된 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.@RestController, @Service, @Repository, @Configuration 어노테이션은 모두 @Component 어노테이션을 가지고 있음 스프링 빈을 주입 받는 방법생성자를 이용해 주입받는 방식 (가장 권장) setter를 통해 주입받는 방식필드에 직접 주입하는 방법권장 Xsetter를 통해 다른 인스턴스로 교체해 동작에 문제가 생길 수 있음필드에 바로 주입하게 되면 테스트가 어려움 과제5클린 코드에 대해 배우고 클린 코드 직접 작성하기https://velog.io/@dmsqls19/인프런-워밍업-클럽-1기-BE-과제5.-클린-코드Clean-Code 느낀 점일단 이번주에 공부를 너무 안 해서,,, 조금 회고록이 짧은 것에 대해 반성해야겠다.그리고 배우면서 느낀건 클린코드가 정말 중요하다는 걸 깨달았다.내가 쓴 코드에 대해 설명할 때 이건 왜 이렇게 구성했는지에 잘 파악하면 된다고 생각했는데 줄일 수 있는 건 최대한 줄이면서 간결하게 하는 것도 중요하다는 걸 느꼈다. 코드가 왜이렇게 복잡하지? 했던 것도 다 줄일 수 있는 것들이었다.배운 것보다 배울 게 더 많다 라는 생각과 함께 걱정은 되지만 새로운 걸 배우는 건 늘 재밌기 때문에 다음주도 기대가 된다.

백엔드

S.M KIM

[인프런 워밍업 클럽 Study] 백엔드 2주차 발자국

학습 내용 요약 <클린 코드>좋지 않은 코드를 작성할 때의 문제점협업에 어려움을 겪게 된다가독성이 떨어져 생산성이 낮아진다코드 수정 시 다른 코드에 끼칠 영향으로 인해 수정이 어려워진다테스트에 어려움을 겪게 된다종합적으로, 유지보수성이 매우 떨어지게 된다 <Layered Architecture>각 클래스가 각자의 역할을 가지고 겹겹이 쌓인 것을 의미한다[ Repository ] → [ Service ] → [ Controller ] + ( DTO ) <Controller와 Spring Container>@RestController public class UserController { private final UserService userService; public UserController(JdbcTemplate jdbcTemplate) { this.userService = new UserService(jdbcTemplate); } } 위 코드에 대한 의문은 다음과 같다.클래스 안에 있는 함수를 사용하기 위해서는 인스턴스화가 필요하다. UserController에 있는 메소드를 API 진입 지점으로 사용하고 있으나 현재 UserController는 인스턴스화가 되어있지 않다.UserController의 생성자는 JdbcTemplate에 의존하고 있으나 JdbcTemplate에 대한 처리 없이 UserController에서 JdbcTemplate를 가져오고 있다.@RestControllerUserController 클래스를 API의 진입 지점으로 설정해준다.UserController 클래스를 스프링 빈으로 등록시킨다.@SpringBootApplication이 다양한 설정들을 모두 자동으로 해준다. 이때 자동으로 해주는 것 중 하나가 스프링 서버 내부에 거대한 컨테이너를 만드는 것이다.컨테이너 안에 클래스가 들어가게 되며 이렇게 들어간 클래스를 스프링 빈이라고 한다. 클래스가 들어갈 때는 이 빈을 식별할 수 있는 이름 및 타입과 함께 다양한 정보들이 저장된다. 그리고 이때 인스턴스화도 함께 이뤄진다.JdbcTemplate 역시 스프링 빈으로 등록되어 있다. build.gradle에 설정했던 spring-boot-starter-data-jpa 의존성이 JdbcTemplate 을 스프링 빈으로 미리 등록해 준 것이다.스프링 컨테이너는 UserController를 인스턴스화할 때, UserController가 필요로 하는JdbcTemplate 을 컨테이너 내부에서 찾아 인스턴스화를 진행해 준다. 만약 이때 JdbcTemplate이 없다면 에러가 발생한다.정리하자면, 서버를 시작하게 되면 다음과 같은 일이 순차적으로 일어난다.컨테이너가 시작된다.컨테이너에 기본적으로 많은 스프링 빈이 등록된다. (ex. JdbcTemplate)스프링을 사용하는 개발자가 설정해 준 스프링 빈이 등록된다. (ex. UserController)이때 필요한 의존성이 자동으로 설정된다. (ex. UserController를 만들 때 JdbcTemplate을 자동으로 넣어준다)Repository와 Service 또한 각각 @Repository와 @Service 어노테이션으로 스프링 빈으로 등록시켜줄 수 있다. <Spring Container를 사용하는 이유>IoC (Inversion of Control, 제어의 역전)프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴을 말한다.IoC의 목적작업을 구현하는 방식과 작업 수행 자체를 분리한다.모듈을 제작할 때, 모듈과 외부 프로그램의 결합에 대해 고민할 필요 없이 모듈의 목적에 집중할 수 있다.다른 시스템이 어떻게 동작할지에 대해 고민할 필요 없이, 미리 정해진 협약대로만 동작하게 하면 된다.모듈을 바꾸어도 다른 시스템에 부작용을 일으키지 않는다.출처 : 위키백과 ( https://ko.wikipedia.org/wiki/제어_반전 ) DI (Dependency Injection : 의존성 주입)하나의 객체가 다른 객체의 의존성을 제공하는 것을 의미한다.DI의 장점의존성 주입은 클라이언트의 구성 가능성을 유연하게 해준다. 클라이언트의 행위는 고정되어 있다. 클라이언트는 클라이언트가 기대하는 고유한 인터페이스를 지원하는 모든 것을 할 수 있다.의존성 주입을 통해 시스템의 구성 세부 사항을 외부의 구성 파일에서 사용하여 리컴파일 없이 시스템을 재구성할 수 있다. 분리된 구성은 컴포넌트의 여러 구현을 요구하는 다양한 상황을 위해 작성될 수 있다. 이는 국한되어 있지는 않지만 테스팅을 포함한다.의존성 주입은 코드의 동작에서의 어떠한 변경도 요구하지 않으므로 리팩터링으로써 레거시 코드에도 적용할 수 있다. 클라이언트는 더 독립적이며 테스트에 속하지 않은 다른 객체를 가장하는 stubs 또는 모의 객체를 사용해 독립된 유닛 테스트가 더 쉬워지는 결과를 얻는다.의존성 주입을 통해 클라이언트는 사용해야하는 모든 구체적인 구현에 대한 지식을 제거할 수 있다. 디자인 변경이나 결함의 영향으로부터 클라이언트를 독립하는데 도움을 주며, 이는 재사용성, 테스트가능성, 유지가능성을 향상시킨다.출처 : 위키백과 ( https://ko.wikipedia.org/wiki/의존성_주입 ) <Spring Container를 다루는 방법>@Primary 와 @Qualifier@Primary : 여러 개의 Bean들 중 사용할 Bean에 붙이는 어노테이션.@Qualifier : Bean에 추가 구분자를 붙여주는 방법으로 생성자에서 해당 구분자를 명시하면 그 구분자를 가진 Bean을 주입해준다.@Primary와 @Qualifier 중에 @Qualifier의 우선순위가 더 높다.@Configuration 와 @Bean@Configuration : 클래스에 붙이는 어노테이션으로, @Bean을 사용할 때 함께 사용해 주어야 한다.@Bean : 메소드에 붙이는 어노테이션으로, 메소드에서 반환되는 객체를 스프링 빈에 등록한다.보통 직접 만든 클래스를 스프링 빈으로 등록할 때에는 @Service나 @Repository를 사용하고, 외부 라이브러리나 프레임워크에 만들어져 있는 클래스를 스프링 빈으로 등록할 때에는 @Configuration + @Bean 조합을 많이 사용하게 된다.@Component@RestController, @Service, @Repository, @Configuration은 모두 @Component 어노테이션을 가지고 있다.@Component 어노테이션을 붙이면 주어진 클래스를 ‘컴포넌트'로 간주하고, 컴포넌트들은 스프링 스프링 서버가 뜰 때 자동으로 감지된다. @Component 덕분에 지금까지 우리가 사용했던 어노테이션들이 모두 자동으로 감지되었던 것이다.@Component 어노테이션은 컨트롤러, 서비스, 리포지토리가 아닌 추가적인 클래스를 스프링 빈으로 등록할 때에도 사용된다. <Spring Bean을 주입받는 방법>1. 생성자를 이용한 주입보통 제일 우선시되는 방법이다.@Repository public class UserRepository { private final JdbcTemplate jdbcTemplate; // 생성자에 JdbcTemplate이 있으므로 스프링 컨테이너가 넣어준다. public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } ... }  2. setter를 이용한 주입setter 메소드에 @Autowired 어노테이션을 사용하여 주입한다.@Repository public class UserRepository { private JdbcTemplate jdbcTemplate; @Autowired public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } ... }  3. 필드에 직접적으로 주입필드 위에 @Autowired 어노테이션을 사용하여 주입한다.@Repository public class UserRepository { @Autowired private JdbcTemplate jdbcTemplate; ... }  4. @Qualifier 어노테이션을 이용한 주입@Qualifier 어노테이션은 Spring Bean을 사용하는 쪽과 등록하는 쪽 모두 사용할 수 있다.@Service @Qualifier("main") public class BananaService { ... } @RestController public class UserController { private final UserService userService; private final FruitService fruitService; public UserController(UserService userService, @Qualifier("main") FruitService fruitService) { this.userService = userService; this.fruitService = fruitService; } ... } 회고이번 주에는 지난 금요일에 들었던 깜짝 특강이 기억에 남았습니다. 테스트 코드에 대한 것은 막연하게 들어만 봤었습니다. 얼마 전에 프로젝트를 진행하면서 팀원이 사용한 것을 보긴 했지만, 당시에 저는 프론트앤드 역할이라 테스트 코드에 대해서 고찰해 볼 기회가 적었습니다. 이렇듯 테스트 코드를 실제로 적용한 적은 없었지만 지난 특강으로 클린코드에 대한 개념과 더불어 테스트 코드의 작성에 대한 개념을 익히게 되어 도움이 많이 되었습니다. 

백엔드워밍업

이용수

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

인프런 워밍업 클럽 2주차 발자국서론벌써 강의를 들은지 2주차가 지나간다. 지난 주차와 마찬가지로 강의와 스터디를 하면서 배운 내용을 잊지 않도록 주마다 블로그에 정리하려고 한다.섹션 3 : 역할의 분리와 스프링 컨테이너UserController와 스프링 컨테이너UserController는 JdbcTemplate에 의존한다.@RestController가 UserController 클래스를 API의 진입지점으로 만들어주고, 스프링 빈으로 등록시킨다.서버 시작 후 동작 과정스프링 컨테이너(클래스 저장소)가 시작된다.기본적으로 많은 스프링 빈들이 등록된다.우리가 설정해준 스프링 빈이 등록된다.필요한 의존성이 자동으로 설정된다.서버 시작 후 세개의 클래스 동작 과정JdbcTemplate을 의존하는 UserRepository가 스프링 빈으로 등록된다.UserRepository를 의존하는 UserService가 스프링 빈으로 등록된다.UserService를 의존하는 UserController가 스프링 빈으로 등록된다.스프링 컨테이너를 사용하는 이유 다음 시간에스프링 컨테이너를 왜 사용할까?스프링 컨테이너를 사용하면 컨테이너가 서비스를 대신 인스턴스화 하고, 알아서 레포지토리를 결정해준다.의존성 주입 (DI, Dependency Injection) : 컨테이너가 선택해 서비스에 넣어주는 과정.제어의 역전 (IoC, Inversion of Control) : 스프링 컨테이너가 어떤 구현 타입을 쓸지 대신 결정해주는 방식.@Primary 를 활용해서 어떤 Repository가 주입될지 조절할 수 있다.스프링 컨테이너를 다루는 방법빈을 등록하는 방법@RestController, @Service, @Repository를 사용하는 방법.@Configuration + @Bean를 사용하는 방법@Configuration : 클래스에 붙이는 어노테이션, @Bean과 함께 사용한다.@Bean : 메소드에 붙이는 어노테이션, 메소드에서 반환되는 객체를 스프링 빈에 등록한다.@Service, @Repository를 사용하는 경우개발자가 직접 만든 클래스를 스프링 빈으로 등록할 때 사용한다.@Configuration + @Bean을 사용하는 경우외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때 사용한다.@Component : 주어진 클래스를 컴포넌트로 간주하여, 이 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.@Component를 사용하는 경우컨트롤러, 서비스, 리포지토리가 모두 아니고, 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용된다.스프링 빈을 가져오는 방법생성자를 이용해 주입받는 방식setter와 @Autowired 사용 누군가 setter를 사용하면 오작동 할 수 있다.필드에 직접 @Autowired 사용 테스트를 어렵게 만드는 요인이다.@Qualifier : 여러 개의 클래스 후보군이 있을때 그 중 특정 클래스를 가져오고 싶은 경우 사용하는 어노테이션스프링 빈을 사용하는 쪽, 등록하는 쪽 모두 @Qualifier를 사용할 수 있다.스프링 빈을 사용하는 쪽에서만 쓰면, 빈의 이름을 적어주어야 한다.양쪽 모두 사용하면, @Qualifier끼리 연결된다.@Primary 와 @Qualifier 중 우선 순위는? 사용하는 쪽에서 직접 적어준 @Qualifier가 우선한다.Section 3 정리좋은 코드가 왜 중요한지 이해하고, 원래 있던 Controller 코드를 보다 좋은 코드로 리팩토링함.스프링 컨테이너와 스프링 빈이 무엇인지 이해함.스프링 컨테이너가 왜 필요한지, 좋은 코드와 어떻게 연관이 있는지 이해함.스프링 빈을 다루는 여러 방법을 이해함.섹션 4 생애 최초 JPA 사용하기문자열 SQL을 직접 사용하는 것이 너무 어렵다SQL을 직접 작성하면 나타나는 문제점문자열을 작성하기 때문에 실수할 수 있고, 실수를 인지하는 시점이 느리다. (컴파일 시점에 발견되지 않고, 런타임 시점에 발견된다.)특정 데이터베이스에 종속적이게 된다.반복 작업이 많아진다. 테이블을 하나 만들 때마다 CRUD 쿼리가 항상 필요하다.데이터베이스의 테이블과 객체는 패러다임이 다르다.JPA (Java Persistence API) : 객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙JPA는 규칙일 뿐이고, 규칙을 실제로 구현한 건 HIBERNATE이며 HIBERNATE 는 내부적으로 JDBC를 사용한다.JPA를 활용해 매핑하는 것 다음 시간에 알아볼 예정.유저 테이블에 대응되는 Entity Class 만들기Java객체와 MySQL 테이블을 매핑.@Entity : 스프링이 User객체와 user테이블을 같은 것으로 바라보게한다.@Id : 이 필드를 primary key로 간주한다.@GeneratedValue : primary key는 자동 생성되는 값이다.JPA를 사용하기 위해서는 기본 생성자가 꼭 필요하다.@Column : 객체의 필드와 테이블의 필드를 매핑한다. null의 여부, 길이 제한, DB의 Column 이름 등을 정의한다. 단 객체의 필드와 테이블의 필드가 동일한 경우 생략할 수 있다.JPA를 사용하기 위한 추가적인 설정application.yml jpa: hibernate: ddl-auto: none // 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지 설정. // create : 기존 테이블이 있다면 삭제 후 다시 생성 // create-drop : 스프링이 종료될 때 테이블을 모두 제거 // update : 객체와 테이블이 다른 부분만 변경 // validate : 객체와 테이블이 동일한지 확인 // none : 별다른 조치를 하지 않는다. properties: hibernate: show_sql: true // JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여줄지 말지. format_sql: true // SQL을 보여줄 때 이쁘게 포맷팅 할지 말지. dialect: org.hibernate.dialect.MySQL8Dialect // 이 옵션으로 DB를 특정하면 조금씩 다른 SQL을 수정해준다. Spring Data JPA를 이용해 자동으로 쿼리 날리기SQL을 작성하지 않고, 유저 생성 / 조회 / 업데이트 기능을 리팩토링하기.UserRepository 인터페이스 생성UserRepository 인터페이스는 JpaRepository를 상속 받음유저 저장 기능 : save 메소드에 객체를 넣어주면 INSERT SQL이 자동으로 날라가게끔 한다.유저 조회 기능 : findAll을 사용해 모든 데이터를 가져오도록 한다.유저 업데이트 기능 : id를 이용해 User를 가져와 User의 유,무를 확인하고, User가 있으면 update 쿼리를 달려 데이터를 수정한다.사용한 기능save : 주어지는 객체를 저장하거나 업데이트 시켜준다.findAll : 주어지는 객체가 매핑된 테이블의 모든 데이터를 가져온다.findById : id를 기준으로 특정한 1개의 데이터를 가져온다.Spring Data JPA : 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리Spring Data JPA를 이용해 다양한 쿼리 작성하기삭제 기능을 Spring Data JPA로 변경하기.Spring Data JPA를 활용해 조회 쿼리를 작성하는 방법public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByName(String name); }반환 타입을 User로, 유저가 없으면 null이 반환되도록 한다.함수 이름만 작성하면, 알아서 SQL이 조립된다.find라고 작성하면, 1개의 데이터만 가져온다.By 뒤에 붙는 필드 이름으로 SELECT 쿼리의 WEHRE문이 작성된다.SELECT * FROM user WHERE name = ?;public void deleteUser(String name) { User user = userRepository.findByName(name) .orElseThrow(IllegalArgumentException::new); userRepository.delete(user); }주어지는 데이터를 DB에서 제거한다.DELETE SQL;다양한 Spring Data JPA 쿼리By 앞에 들어갈 수 있는 구절find : 1건을 가져온다. 반환 타입은 객체 또는 Optional<타입>이 될 수 있다.findAll : 쿼리의 결과물이 N개인 경우 사용한다. List<타입> 을 반환한다.exists : 쿼리 결과가 존재하는지 확인한다. Boolean 타입으로 반환한다.count : SQL의 결과 개수를 센다. Long타입으로 반환한다.By 뒤에 들어갈 수 있는 구절 (AND, OR로 조합 가능)GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이에StartsWith : ~로 시작하는EndsWith : ~로 끝나는트랜잭션에 대해 다음 시간에 알아볼 예정.트랜잭션 이론편트랜잭션 : 쪼갤 수 없는 업무의 최소 단위 모두 다 성공시키거나 모두 다 실패시키거나 하기 위한 것.트랜잭션 시작 : start transaction;트랜잭션 종료 : commit;트랜잭션 실패 처리 : rollback;트랜잭션을 통해 하나의 함수에서 여러 개의 Insert 쿼리 중 모두 성공하면 반영하고, 하나라도 실패하면 반영이 안되도록 할 수 있다.트랜잭션 적용과 영속성 컨테스트서비스 메소드가 시작할 때 트랜잭션이 시작된다.서비스 메소드 로직이 모두 정상적으로 성공하면 commit이 된다.서비스 메소드 로직 실행 도중 문제가 생기면 rollback 되도록 한다.@Transactional 으로 간단하게 적용 가능하다.조회만 하는 SQL의 경우 @Transactional(readOnly = true) 로 읽기만 가능하도록 할 수 있다.IOException과 같은 Checked Exception은 롤백이 일어나지 않는다.영속성 컨텍스트 : 테이블과 매핑된 Entity 객체를 관리/보관하는 역할스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.영속성 컨텍스트의 특수 능력 4가지변경 감지 (Dirty Check) : 영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도, 변경을 감지해 자동으로 저장된다.쓰기 지연 : DB의 INSERT / UPDATE / DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번만 날린다.1차 캐싱 : ID를 기준으로 Entity를 기억하고, 캐싱된 객체는 완전히 동일하다.Section 4 정리문자열 SQL을 직접 사용하는 것의 한계를 이해하고, 해결책인 JPA, Hibernate, Spring Data JPA가 무엇인지 이해한다.Spring Data JPA를 이용해 데이터를 생성, 조회, 수정, 삭제할 수 있다.트랜잭션이 왜 필요한지 이해하고, 스프링에서 트랜잭션을 제어하는 방법을 익힌다.영속성 컨텍스트와 트랜잭션의 관계를 이해하고, 영속성 컨텍스트의 특징을 알아본다.섹션 5 책 요구사항 구현하기책 생성 API 개발하기요구사항 : 도서관에 책을 등록할 수 있다.API 스펙HTTP Method : POSTHTTP Path : /bookHTTP Body (JSON) :{ "name": String //책 이름 }결과 반환 : 상태코드 200대출 기능 개발하기요구사항 : 사용자가 책을 빌릴 수 있다. 단 다른 사람이 그 책을 빌렸다면, 빌릴 수 없다.API 스펙HTTP Method : POSTHTTP Path : /book/loanHTTP Body (JSON) :{ "userName": String "bookName": String }결과 반환 : 상태코드 200반납 기능 개발하기요구사항 : 사용자가 책을 반납할 수 있다.API 스펙HTTP Method : POSTHTTP Path : /book/returnHTTP Body (JSON) :{ "userName": String "bookName": String }결과 반환 : 상태코드 200지금 까지 작성한 코드를 조금 더 객체지향적으로 만드는 방법User와 UserLoanHistory가 직접 협업할 수 있게 처리하는 방법 다음 시간에 알아볼 예정.Section 5 정리책 생성, 대출 반납 API를 온전히 개발하며 지금까지 다루었던 모든 개념을 실습해 봤다.2주차 과제과제 4번 : https://www.inflearn.com/blogs/7855과일 정보를 저장하는 API 만들기API에서 long을 사용한 이유팔린 과일 정보를 기록하는 API 만들기특정 과일을 기준으로 팔린 금액, 팔리지 않은 금액을 조회하는 API 만들기sum, group by 키워드를 사용해 구현해보기과제 5번 : https://www.inflearn.com/blogs/7873제시된 주사위 코드를 클린 코드로 변환해보기숫자 범위가 달라지더라도 동작되도록 변환해보기2주차 후기지난주 1주차 회고록에 이어서 벌써, 인프런 워밍업 클럽 과정의 2주차가 마무리됬다. 이번 주차에서는 섹션 3의 일부분 부터 섹션 5의 일부분의 강의를 수강할 수 있었다. 간단하게 각 섹션 별로 강의의 주제를 다시한번 상기하고 어떤걸 배우고 느꼈는지 확인해보려고 한다.섹션 3의 주제 : 역할의 분리와 스프링 컨테이너역할의 분리에 대해서는 이전 회고록에서 상세히 다루었기 때문에 다루지 않겠다.스프링 컨테이너와 스프링 빈에 무엇인지 이해하고 어떻게 동작하는지 구조를 파악할 수 있었으며, 스프링 컨테이너를 사용하는 것이 좋은 코드와 어떻게 연관이 있는지 이해하고 스프링 빈을 다루는 여러 방법을 배울 수 있었다.섹션 4의 주제 : 생애 최초 JPA 사용하기문자열 SQL을 직접 사용하는 것의 한계를 이해하고, 해결책인 JPA, Hibernate, Spring Data JPA가 무엇인지 이해할 수 있었다. Spring Data JPA를 이용해 데이터를 생성, 조회, 수정, 삭제해 볼 수 있었다. 트랜잭션이 왜 필요한지 이해하고, 스프링에서 트랜잭션을 제어하는 방법을 익혔다. 영속성 컨텍스트와 트랜잭션의 관계와 특징을 이해할 수 있었다.섹션 5의 주제 : 책 요구사항 구현하기책 생성, 대출 반납 API를 온전히 개발하며 지금까지 다루었던 모든 개념을 실습해 볼 수 있었다.깜짝 라이브 강의5번 과제인 클린 코드로 변환하는 방법에 대해 깜짝 라이브 강의를 열어 자세한 코드 리뷰를 진행했다. 코치님이 직접 코드를 리팩토링 하는 과정을 지켜보고 따라해보며 크게 느낀점이 있었는데, 내가 항상 클린 코드를 만든다고 만들지만 그 끝은 끝이 없구나 하고 생각이 들었다.일단 코치님이 진행한 수준까지 라도 충분히 이해하고 적용하는 것까지 진행하는 것이 우선적이라 생각이 들었다.그 외에도 코치님과 비슷하게 리팩토링을 수행한 수강생에 대해 알려주셨는데, 분명히 다른 사람 코드를 살펴보고 이해하는 것이 중요하다고 생각했고, 다른 수강생의 코드를 보고 워밍업 클럽 커뮤니티에서 서로 물어볼 수 있다는 부분에 있어 그렇게 학습하는 것이 강의 취지에 맞게 학습하는 것이라 생각했다.마무리이번 2주차 강의를 듣고 회고록을 작성하면서 앞으로도 지금처럼 성실함을 중점으로 꾸준하게 강의를 듣고 기록하는 것이 중요하다고 생각했다. 다음주는 벌써 배포에 대해 배우는 시점이 온다. 개인적으로 제일 관심이 있던 부분이기 때문에 더 확실하게 이해하기 위해 주말에 미리 선행을 진행 해보려고 한다.

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

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

섹션 4SQl을 직접 작성하면 어떤 점이 아쉬울까?문자열을 작성하기 때문에 실수할 가능성 Up!실수를 인지하는 시점 Slow~컴파일 시점에 발견X, 런타임 시점 발견되기 때문에 오래걸림 특정 데이터 베이스에 종속됨반복작업 多테이블 하나 만들 때마다 CRUP 쿼리가 항상 필요데이터베이스의 테이블과 객체는 패러다임이 다르다.JPA ( Java Persistence API )객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 보관하기 위해 Java 진영에서 정해진 규칙영속성서버가 재시작되어도 데이터는 영구적으로 저장되는 속성 API정해진 규칙 ORM ( Object-Relational Mapping )객체-관계 매핑의 줄임말 장점완벽한 객체지향적인 코드재사용, 유지보수, 리팩토링 용이성DBMS(DataBase Management System) 종속성 하락단점ORM이 모든 걸 해결X객체 - 관계 간의 불일치세분성 ( Granularity )경우에 따라서 DB에 있는 테이블 수보다 더 많은 클래스를 가진 모델이 생길 수 있음상속성 ( Inheritance )RDBMS는 객체지향 프로그래밍 언어의 특징인 상속 개념이 X일치 ( Identity )RDBMS는 기본키를 이용하여 동일성을 정의, 자바는 객체 식별( a==b )과 객체 동일성 ( a.equals(b))을 모두 정의한다.연관성 ( Associations )객체지향 언어는 방향성이 있는 객체의 참조( reference )를 사용하여 연관성을 나타내지만 RDBMS는 방향성이 없는 외래키를 이용해서 나타냄탐색 ( Navigation )자바와 RDBMS에서 객체를 접근하는 방법이 근본적으로 다르다. 자바는 그래프형태로 하나의 연결에서 다른 연결로 이동하며 탐색한다. 그러나 RDBMS에서는 일반적으로 SQL문을 최소화하고 JOIN 을 통해 여러 엔티티를 로드하고 원하는 대상 엔티티를 선택하는 방식으로 탐색Hibernate란?하이버네이트는 자바 언어를 위한 ORM 프레임워크JPA의 구현체로, JPA 인터페이스를 구현하며, 내부적으로 JDBC API를 사용JPA는 관계형 데이터베이스와 객체의 패러다임 불일치 문제를 해결할 수 있다는 점과 영속성 컨텍스트(엔티티를 영구 저정하는 환경) 제공이 큰 특징 장점생산성 Hibernate는 SQL을 직접 사용하지 않고, 메서드 호출만으로 쿼리가 수행 => SQL 반복작업을 하지 않음으로 생산성 UP!SQL을 몰라도 되는 건 아님유지보수테이블 컬럼이 변경되었을 때, 테이블과 관련된 DAO 파라미터, 결과, SQL 등을 대신 수행 => 유지보수 UP! 특정 벤더에 종속적이지 않음JPA는 추상화된 데이터 접근 계층을 제공 => 특정 벤더에 종속적이지 않음설정 파일에서 JPA에게 어떤 DB를 사용하고 있는지를 알려주기만 하면 DB 변경 가능패러다임 불일치 해결상속, 연속 관계, 객체 그래프 탐색, 비교 등 객체와 관계형 데이터베이스와의 패러다임 불일치를 해결 단점성능메서드 호출만으로 쿼리를 수행하는 것은 직접 SQL을 작성하는 것보다 성능상 좋지 않음세밀함메서드 호출만으로 DB 데이터를 조작하기에는 한계 有 => 보완하기 위해 JPQL을 지원NativeQuery를 지원하여 SQL 자체 쿼리도 작성할 수 있음러닝커브공부할거 多JPA 어노테이션@Entity스프링이 User객체와 user 테이블을 같은 것으로 바라봄@Id이 필드를 primary key로 간주함@GeneratedValueprimary key는 자동 생성되는 값MySQL의 auto_increment를 사용 = IDENTITY@Column객체의 필드와 Table의 필드를 매핑생략도 가능!! JPA를 사용하기 위해 기본 생성자 꼭 필요!!!save => 주어지는 객체를 저장하거나 업데이트 시켜줌findAll => 주어지는 객체가 매핑된 테이블의 모든 데이터를 가져옴findById => id를 기준으로 특정한 1개의 데이터를 가져온다.application.yml 세팅Spring Data JPA복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리By 앞에 들어갈 수 있는 구절 정리find : 1건을 가져온다. 반환 타입은 객체가 될 수도 있고, Optional<타입>이 될 수도 있다.findAll : 쿼리의 결과물이 N개인 경우 사용. List<타입> 반환exists : 쿼리 결과가 존재하는지 확인. 반환 타입은 booleancount : SQL의 결과 개수를 센다. 반환 타입은 long이다.GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이에StartsWith : ~로 시작하는EndWith : ~로 끝나는트랜잭션이란?@Transactional쪼갤 수 없는 업무의 최소 단위commit => 트랜잭션 성공!rollback => 트랜잭션 실패ㅠ! 주의 사항 !IOException과 같은 Checked Exception은 롤백이 일어나지 않는다.영속성 컨텍스트란?테이블과 매핑된 Entity 객체를 관리/ 보관하는 역할=> 스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.특수 능력 4가지// 영속성 컨텍스트의 특수 능력 4가지 // 1 . 변경감지 (Dirty Check) // 영속성 컨텍스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라도, 변경을 감지해 자동으로 저장된다. // 2. 쓰기 지연 // DB의 INSERT / UPDATE / DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번만 날린다. // 3. 1차 캐싱 // ID를 기준으로 Entity를 기억한다! // 이렇게 캐싱된 객체는 완전이 동일하다! // 4. 지연로딩(Lazy Loading) // @OneToMany의 fetch 옵션 // 지연로딩을 사용하게 되면, 연결되어 있는 객체를 꼭 필요한 순간에만 가져온다색션 4 회고JPA를 사용하면 더 편리하고 더 단순하게 코드를 사용할 수 있으나 JPA에 대한 공부를 꼼꼼하게 해야 올바르게 작성할 수 있을거 같고 트랜잭션의 기능이 처음 듣는 개념이지만 스프링을 이용할 때 너무나 좋은 기능이고 이 기능을 프로젝트에 적용시켜 보고 싶었습니다.섹션 51. 연관관계를 사용하면 무엇이 좋을까??각자의 역할에 집중하게 된다! (= 응집성)새로운 개발자가 코드를 읽을 때 이해하기 쉬워진다.테스트 코드 작성이 쉬워진다.2. 연관관계를 사용하는 것이 항상 좋을까?지나치게 사용하면, 성능상의 문제가 생길 수도 있고도메인 간의 복잡한 연결로 인해 시스템을 파악하기 어려워질 수 있다.또한 너무 얽혀 있으면, A를 수정했을 때 B C D 까지 영향이 퍼지게 된다.비즈니스 요구사항, 기술적인 요구사항, 도메인 아키텍처 등 여러 부분을 고민해서 연관관계 사용을 선택해야 한다.! 원하는 기능에 대한 Entity는 필요한 서비스기능마다 하나씩 적용해서 하자!!=> 바뀔 경우 하나의 Entity만 수정하는 방향이 좋기 때문색션 5 회고색션 4의 기능들을 활용하는 색션이였고 이번 수업을 진행하면서 오타를 많이 내서 시간 지연이 많이 되었지만 더 강의를 오래 볼 수 있게 되서 좋았고 4-5색션에 중요성이 큰거 같고 더 꼼꼼히 해보고 혼자서 더 적용시켜보는 실습을 많이 진행해 보아야겠습니다.과제 4https://www.notion.so/4-7697c4ff27e24d8796886fd8fa5d622a=> 배운 내용을 작게나마 실습해본 과제라고 생각합니다. 실습을 통하여 흘러가는 거를 생각해본 계기가 되었습니다. 과제 5https://www.notion.so/5-260b9f545f2942a79ba1f9308303dba1=> 주사위에 대해 어떤 방식을 통해 할지 고민하다 Map을 활용하면 더 깔끔하게 나올거같아 Map에 대해 공부하면서 적용시켜 보았습니다. 그리구 성공하게 되서 Map에 대해 알게된 과제가 되었습니다.

백엔드

이삭토스트

[인프런 워밍업 클럽 스터디1기] BE 2주차 회고록

강의 수강6일차 - 스프링 컨테이너의 의미와 사용 방법우리가 설정해줘야 하는 부분들이 설정을 안 해줬는데도 작동하는 신기한 현상을 볼 수 있다.물론 정답은 스프링 부트에서 제공해주는 매우 편리한 기능(스프링 빈, 컨테이너)에 의한거지만 (개발자는 반복을 싫어한다.)또한 기기 자체에서 모듈방식을 채택하는 경우가 많은데 그것과 유사하게 스프링 내에서는Repository를 어떤 전략을 취할 것인지 선택하여 많은 변경이 없이 구조를 효과적으로 바꾸게 된다.스프링 컨테이너가 이 역할을 해주고 컨테이너가 서비스를 대신 인스턴스화하고, 그때마다 레포지토리를 결정해주는데 이 방식 자체를 IoC라고 한다.@Configuration과 @Component등 스프링에서 자주 쓰이는 어노테이션에 대해서 배우게 되는 시간이었던 것 같다.7일차 - Spring Data JPA를 사용한 데이터베이스 조작SQL에서 벗어나 드디어 JPA를 본격적으로 사용하게 되는 부분이다.JPA를 사용하기 위한 사전 설정과 Entity 클래스, 그리고 JPA를 사용할 때 가장 중요한 ddl_auto 이 부분이 실무에서는 매우 중요하다고 생각한다. (실무 데이터에서 validate빼고 사용한다면 데이터가, 변형 또는 날라가버리니까)가끔 일반적인 SQL 명령어에서 정밀하게 다루고자 할 때 JPA를 확장시켜둔 Repository에JPA 쿼리를 이용하여 다양한 쿼리를 작성해줄 수 있다. (조금 더 정밀하게 작동시키려면 JPQL이 답)8일차 - 트랜잭션과 영속성 컨텍스트트랜잭션의 개념에 대해 알아가는 구간, 영속성 컨텍스트 특징 (변경 감지, 쓰기 지연, 1차 캐싱, 지연로딩, 동일성 보장) 9일차 - 조금 더 복잡한 기능을 API로 구성하기User에서 벗어나 대출과 반납이라는 개념을 들어간다. 새로운 테이블을 만들고 두 테이블을 연결해서 사용하는 방법을 알게 되었다.그리고 마지막으로 JPA에서 연관관계 매핑에 관한 떡밥을 남기시며 다음 주 월요일을 기대하게 된다.미션6일차 과제 - 4일차 과제 API 분리, Controller - Service - Repository 계층에 익숙해지기 기존코드를 JPA를 이용하여리팩토링을 해나가는 과정이다.문제1 에서는 컨트롤러가 너무 많은 역할을 하고 있어 Controller(HTTP 관련 역할) - Service(분기 처리, 로직) - Repository(DB와 접근 담당)로 나누는 과정이며,문제2는 Repository를 인터페이스화 해서 메모리에 저장하는 부분과 MySql 저장하는 것을 선택하게끔 하는 것이었다.문제1은 기존 코드를 Controller에서 Service로 우선 나누고 그 다음에 Service에서 DB와 접근에 관련된 부분을 분리하여 Repository에 나누는등 3개로 분리하였다. 이 과정에서 JdbcTemplate를 Repository만 접근하므로 코드의 가독성을 늘리기 위해Repository를 제외하고는 JdbcTemplate 관련 설정을 지워줬다.문제2는 DIP에 관련된 내용으로 스프링에서 클래스에 의존하는 것이 아니라 인터페이스에 의존하게끔 만들어 의존성 역전 원칙을 만들게 하기 위함이다. (+ IoC)7일차 과제 - JPA 연습 문제1은 6일차 과제에서 만든 과제를 JdbcTemplate에서 Spring Data JPA를 사용하는 환경으로 바꿔주는 과정이다.JPA를 처음 접할 때 이걸 왜 사용하고 어떻게 사용해야하는지 몰랐다. 오히려 SQL 내용을 배우고나서 JPA로 천천히 넘어오니 사용방법과 필요성을 뼈저리게 느꼈다. 부트캠프에서는 처음 접하는 내용을 바로 활용하는 입장에 있어서 이해를 하지 못하고 급하게 넘어왔던 부분이었는데 지금 JPA를 다시 공부하면 깊게 배우면서 조금 더 내 실력을 늘릴 수 있을 것 같다. 자연스럽게 테이블에 대한 개념도 배우면서 어떤 곳에서 어떤 코드를 짜고 그 의문점에 대해서 해소 할 수 있는 과제 였다.문제2는 JPA를 적용해 놨다면 이제 새로운 기능을 JPA를 이용하여 만드는 것이다.조금 의아한거라면 JPA를 깊게 배워갈 수록 SQL의 필요성을 느끼게 되는 것 같다.문제3은 2가지 쿼리를 받아서 2가지 갈래로 분기를 만들어 값을 리턴하는 기능을 만드는 것이었다.처음에 dto 설계를 잘 못해서 객체를 객체로 감싸서 List 자체만이 나오는 것이 아니라 dto에서 설정한 이름이 한번 더 포장되어서 결과가 반환되었다. API에서 디버그를 사용하면 요청이 보내졌을 때 변해지는 과정을 알 수 있으면 좋겠다. 아니면 내가 알지 못하는 사용법이 있는 것 같다.8일차 과제 - 미니프로젝트 도입아직은 특별하게 작업물을 만들지는 않았다. 5월 23일까지 제출해야하는 것이므로 프로젝트 전체적으로 크게 보고나서 설계하고 만들어가는 것도 늦지 않다고 생각을 하고, 강의를 들어가면서 구축해나가야 할 것 같다.본격적인 내용은 3주차 회고에서 나오게 될 것 같다.기존 구조에서 새로운 것을 추가하고 JPA를 다양하게 사용해보고 이제 연관관계 매핑이나 백엔드를 살짝 벗어나는 내용도 나오게 될 것 같다. 특히 배포 부분에 관련된 내용 같은데 이 부분은 아는게 많이 없어서 접해보는 걸로 만족하는 계기가 되지 않을까?

백엔드워밍업스터디BE1기회고록

eueun05

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

학습 내용1. 강의1일차: 스프링 컨테이너, 스프링 빈의 개념에 대해 학습하고, 왜 스프링 컨테이너가 좋은 코드와 관련이 있는지 알아봤다. 그리고 의존성 주입방법에 대해 학습했다.2일차: 기존의 문자열 SQL을 활용하는 방식의 한계점을 알아봤고, 이를 해결하기 위해 Spring Data JPA를 이용하는 방법에 대해 학습했다.3일차: 트랜잭션의 개념에 대해 배우고, @Transactional 어노테이션을 사용해 서비스 레이어의 메서드에서 트랜잭션이 보장될 수 있도록 적용해봤다. 그리고 영속성 컨텍스트에 대해 알아봤다.스프링에서 트랜잭션을 시작하면 영속성 컨텍스트가 생기고, 트랜잭션이 종료될 때 영속성 컨텍스트도 종료된다.영속성 컨텍스트 특징변경 감지(Dirty Check): 영속성 컨텍스트 안의 Entity는 변경을 감지해 자동으로 저장된다.쓰기 지연: SQL들을 트랜잭션이 commit될 때 한 번에 날린다.1차 캐싱: 영속성 컨텍스트 안의 Entity를 저장하여 같은 엔티티 조회 시 DB 대신 이미 저장되어 있는 내용에서 조회한다. 4일차: 이전까지 배운 내용들을 종합하여 책 생성, 대출, 반납 기능 API 개발 실습을 했다.5일차: JPA에서 연관관계를 매핑하는 방법에 대해 학습하고, 도메인 계층에 비즈니스 로직을 넣도록 기존의 대출, 반납 기능 코드를 리팩토링했다.2. 과제과제4: 과일정보관리 API 개발과제5: 코드 리팩토링 연습

이혜리

[인프런 워밍업 클럽 스터디1기] 백엔드 - 테스트코드(feat.4차과제)

과제4는 과일 가게에 입고된 과일에 대한 API 를 만드는 과제이다.이 과제로 테스트코드를 작성해보았다.새로운 과일을 등록하는 API (post) 를 테스트하는 부분과이미 등록된 과일을 팔아 saled 필드를 0으로 바꾸는 API (put) 을 테스트하는 부분으로 나누어 2개를 작성하였다.FruitControllerTest.javapackage com.group.libraryapp.controllerTest; import com.fasterxml.jackson.databind.ObjectMapper; import com.group.libraryapp.dto.fruit.FruitCreateRequest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import java.time.LocalDate; import static org.springframework.test.web.client.match.MockRestRequestMatchers.content; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; @ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc public class FruitControllerTest { @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @Test @DisplayName("과일 새로 등록 테스트(성공)") public void saveFruitTest() throws Exception{ //given String name = "자두"; LocalDate date = LocalDate.parse("2024-05-12"); long price = 3000; FruitCreateRequest fruitCreateRequest = new FruitCreateRequest(name,date,price); String url = "http://localhost:8080" + "/api/v1/fruit"; //when final ResultActions resultActions = mockMvc.perform(post(url) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(fruitCreateRequest)) ).andDo(print()); //then resultActions .andExpect(status().isOk()); } @Test @DisplayName("과일 판매후 등록 테스트(성공)") public void saledFruit() throws Exception{ //given String url = "http://localhost:8080" + "/api/v1/fruit"; //when final ResultActions resultActions = mockMvc.perform(put(url) .queryParam("id","9") ).andDo(print()); //then resultActions .andExpect(status().isOk()); } }테스트코드에서는 실제 객체와 비슷하지만 테스트에 필요한 기능만 가지는 가짜 객체를 만들어서 애플리케이션 서버에 배포하지 않고도 스프링 MVC 동작을 재현할 수 있는 클래스를 사용한다.어지저찌 작성해서 테스트는 통과했다.테스트가 실패하는 경우와 나머지 api에 대해서도 추후 추가할 예정이다.아래 블로그들 내용을 참고해서 작성해보았는데아직 모르는게 너무 많은 것 같다.. https://shinsunyoung.tistory.com/52https://velog.io/@minseokangq/REST-API-CRUD 

백엔드테스트코드4차과제백엔드mock

김설하

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

일주일 동안의 학습 방식을 4L식으로 회고해본다.Liked (좋았던 점) + Learned (배운 점) : :중간 점검 : 여러가지 인사이트를 얻을 수 있었다. 능동적으로 생각하는 데에 방해가 될 것 같다는 우려를 하고 있어서 챗GPT의 사용을 어떻게 해야할 지 질문드렸는데 강사님께서 답변주신 내용이 도움이 되고 있다. 내용인즉 활용하는 데에 능숙해지는 것도 나의 능력이고 도움을 받아서 같이 만들 수도 있는 것이다. 다른 동료분들의 Q&A도 많은 도움이 된다. 프로젝트 Tip) 회원가입하고 로그인 후 메인 페이지로 접속 가능한 프로젝트들을 만드는 경우가 있는데, 최대한 게스트 로그인과 같은 접근 용이한 방법을 사용해서 프로젝트를 구성하는 것이 좋다. (회원가입-로그인이 필수면 유저가 메인 기능만 가볍게 열람하고 싶은 경우 많은 손이 들기 때문에...) 또한 기능 동작하는 영상을 짧고 굵게 잘 찍어둔다.과제 시간이 많이 걸린다면 시간이 많이 걸리는 이유를 분석하라 -> 나의 경우엔 CSS에서 은근 시간을 많이 쓰고 중간중간에 코드를 쪼개면서 리팩터링하거나 다른 방법이 없나 모색하는 데에 시간이 걸린다.코딩은 오픈북이라는 말씀강의 : React 시작 | React 파트에서 항상 들으면 까먹게 되고, 심지어 코드로 잘 활용하지 못하는 부분이 최적화라고 생각했다. 섹션3 최적화 파트를 집중해서 들었다.미션 : 아이콘 폰트나 CSS 작성에 도움이 되는 사이트 등 아는 방법들을 적극 활용해서 마음에 드는 UI로 과제를 만들고 있다. 또한 많은 과제들을 관리하기 위해서 Github Project, Milestones 를 활용하고 있다. 한 눈에 보이고 이슈와 연동이 되기도 하고 좋은 방법인 것 같다.  이런 느낌으로 열심히 CSS 구현.. ◠.◠ (근데이제 빠르게 하고싶다는 욕망이 스물스물 올라오는 것이다) Lacked (부족한 점) : 강의는 React 진도를 나가고 있지만. JS 과제인 Github Finder 과제를 수행하고 있다. 기능 위주로 구현해서 제출하면 된다는 걸 알고 있지만 UI도 욕심나고 ㅎㅎ,, 이것저것 챙기고 싶은 게 많아서 지연되는 경향이 있다. 욕심을 내면 한도 끝도 없다는 게 장점이자 단점인 것 같다. 또한해 놓은 미션이 있어도 바로 다음 미션에 착수하거나 다른 할 일들에 밀려서 발자국 작성이 느리다. 문서 작성이 장벽처럼 느껴지는데 문득 소스코드를 열람하는 수고를 들일 사람이 몇이나 있을까 하는 생각이 들었다. 정성들여 짜놓은 코드라면 README.md 작성을 통해 어필하는 것을 아끼지 말아야겠다. Longed For (바라는 점) : 스터디 끝까지 최대정진 오늘까지 JS 과제 완수한 후 발자국 작성, 다음주에는 강의 진도를 앞당겨 듣고 React 과제 선택과 집중하기React에서 상대적으로 취약한 TDD 파트 집중해서 듣기 / (개인적인 욕심) Docker로 실행하는 부분 깊게 이해하면서 듣기이번 주에는 시간을 많이 내지 못하였는데, 다음주는 유종의 미를 걷는 것에 집중하기 시간을 많이 할애하여 임하고 다음주가 지나면 '나의 작업 스타일은 어떤지' 파악이 된 상태였으면 좋겠다.      

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