블로그

구르밍

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

UserController의 의아한점static이 아닌 코드를 사용하려면 인스턴스화 (new)가 필요하다UserController를 인스턴스화하고 있지 않은데 누가 하고 있는것인가???UserController는 JdbcTemplate에 의존하고 있다. 그런데 JdbcTemplate란 클래스도 설정해준적이 없다..! 어떻게 가져온거지?⇒ @RestController가 다해주고 있었다!!!!!⇒ UserController클래스를 API의 진입지점으로 만들 뿐 아니라 UserController 클래스를 스프링 빈으로 등록 시킨다. 스프링 빈(Bean)이란?서버가 시작되면, 스프링 서버 내부에 거대한 컨테이너를 만들게 되고, 컨테이너 안에는 클래스가 들어가게 된다.!이때 다양한 정보도 함께 들어있고, 인스턴스화도 이루어진다.JdbcTemplate도 스프링 빈에 등로되어 있었기에 사용할 수 있었다. (dependencies로 의존)UserRepository는 JdbcTemplate을 가져오지 못할까?JdbcTemplate을 가져오려면 UserRepository가 스프링 빈이어야 하는데 UserRepository는 스프링 빈이 아니다!!→ UserRepository를 스프링 빈으로 등록하자!스프링 컨테이너를 왜 사용할까?메모리에 저장하는 레포를 만든다고 가정하고 코딩…→ MySQL로 저장하고 싶은데?→ 관련된 모든 것들을 다 MySQL로 변경하는 레포로 변경해야함!!! 데이터를 메모리에 저장할지, MySQL에 저장할지 Repository의 역할에 관련된 것만 바꾸고 싶은데 BookService까지 바꿔야 한다. (OMG)⇒ Java의 interface를 활용하자!그래도 Service를 수정해야하는 상황이 발생.. 스프링 컨테이너를 사용하면?BookMemoryRepository, BookMySqlRepository 둘 중 BookService에 쓰일 것을 컨테이너가 선택한다!!→ 이런 방식을 제어의 역전(IoC, Inversion of Control)이라 한다.→ 컨테이너가 선택해 BookService에 넣어주는 과정을 의존성 주입(DI, Dependency Injection)라고 한다.@Primary를 붙여주면 해당 어노테이션이 있는 곳이 사용된다. (우선권 결정)⇒ 나중에 바꿔야 Memory에서 mysql로 바꾸는 작업이 있어서 코드를 다 바꿔야 할 경우 @Primary를 붙여 스프링이 자동으로 해당 레포로 선택하게 하면 된다! 스프링 컨테이너를 다루는 방법빈을 등록하는 방법@Configuration클래스에 붙이는 어노테이션@Bean을 사용할 때 함께 사용해 주어야 한다.@Bean메소드에 붙이는 어노테이션메소드에서 반환되는 객체를 스프링 빈에 등록한다.UserRepository.java에 있는 @Repository 어노테이션 삭제 후 @Bean으로 등록언제 @Service, @Repository를 사용해야하나??→ 개발자가 직접 만든 클래스를 스프링 빈으로 사용할 때@Configuration, @Bean는 언제 사용?→ 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때@Component주어진 클래스를 ‘컴포넌트’로 간주한다.이 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.→ 사실 숨겨져 있었다.. 타고 들어가면 사용되고 있는것을 확인할 수 있다.→ 컨트롤러, 서비스, 리포지토리가 모두 아니고, 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용되기도 한다. 스프링 빈을 주입 받는 몇 가지 방법생성자를 이용해 주입받는 방식 (가장 권장)기존에는 @Autowired 어노테이션을 붙여야했는데 스프링 버전이 업데이트 되면서 안붙여도 자동으로 등록되게 되었다.setter와 @Autowired 사용 → setter를 사용할 경우 오류발생 확률 높아짐필드에 직접 @Autowired 사용 → 테스트하기 어렵다.@Qualifier: 여러개의 후보군이 있을때 그 중 하나를 특정해서 가져올 수 있게 끔 한다.스프링 빈을 사용하는 쪽, 스프링 빈을 등록하는 쪽 모두 @Qualifier를 사용할 수 있다.스프링 빈을 사용하는 쪽에서만 쓰며, 빈의 이름을 적어주어야 한다.양쪽 모두 사용하면, @Qualifier끼리 연결된다.@Primary vs @Qualifier → 동시에 사용할 경우 어떤게 우선일까?사용하는 쪽에서 직접 적어준 @Qualifier가 이긴다.SQL을 직접 작성하게되면..SQL을 직접 작성할 경우 오류가 나도 확인하기 힘들다⇒ 컴파일 시점에 발견되지 않고 런타임 시점(서버가 이미 가동된 후)에 발견된다특정 데이터베이스에 종속적이게 된다.⇒ 다른 DB를 사용할경우 다 바꿔줘야 한다.반복 작업이 많아진다. 테이블을 하나 만들 대마다 CRUD쿼리가 항상 필요하다.데이터베이스의 테이블과 객체는 패러다임이 다르다.→ JPA( Java Persistence API) 등장!: 객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙 유저테이블에 대응되는 Entity Class 만들기@Entity 어노테이션을 User 클래스에 붙인다.→ @Entity : 스프링이 User객체와 user 테이블을 같은 것으로 바라본다. (저장되고, 관리되어야 하는 데이터)우선 테이블의 PK인 id를 만들어보자@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null;@Id : 이 필드를 primary key로 간주한다.@GeneratedValue : primary key는 자동 생성되는 값이다.(strategy = GenerationType.IDENTITY)여기 부분은 DB종류마다 자동생성 전략이 다르다!! 주의!!MySQL의 auto_increment를 사용했기에 IDENTITY를 매칭@Entity를 사용할 경우 - 매개변수가 하나도 없는 기본 생성자가 꼭 필요하기 때문에 다음 코드를 작성!protected User() {}@Column : 객체의 필드와 Table의 필드를 매핑한다.→ null이 들어갈 수 있는지 여부, 길이 제한, DB에서의 column이름 등등…@Column(nullable = false, length = 20, name = "name") //name varchar(20) private String name;null이 들어갈 수 없거, 길이는 20자, 필드의 이름은 name (이 경우는 name = “name” 동일하기에 생략 가능) @Column 은 생략가능하기 때문에 널이 들어갈 수 있고 굳이 특정할필요 없다면 생략이 가능하다. JPA 설정 추가 - application.ymljpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql : true dialect: org.hibernate.dialect.MySQL8Dialectddl-auto : 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지none 외에 다른 종류create : 기존 테이블이 있다면 삭제 후 다시 생성create-drop : 스프링이 종료될 때 테이블을 모두 제거 -… 사용 조심할것 ! 데이터가 모두 날라갈 수있음.update : 객체와 테이블이 다른 부분만 변경validate : 객체와 테이블이 동일한지 확인none : 별다른 조치를 하지 않는다.show_sql : JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여줄 것인가format_sql : SQL을 보여줄 때 예쁘게 포맷팅 할 것인가dialect : 방언, 사투리.. → 이 옵션으로 DB를 특정하면 조금씩 다른 SQL을 수정해준다. Spring Date JPA를 이용하여 자동으로 쿼리 날리기기존 UserRepository.java를 UserJdbcRepository.java로 변경하고domain > user > UserRepository 인터페이스를 생성하자생성 후 JpaRespository를 상속 받는다.public interface UserRepository extends JpaRepository<User, Long> {}<User, Long> → User의 primary key인 Id의 타입이 Long이라 Long으로 작성한다. 유저 데이터 정보 추가하기 (INSERT)public void saveUser(UserCreateRequest request) { userRepository.save(new User(request.getName(), request.getAge())); }JpaRepository를 상속 받는 userRepository에 save메소드를 객체에 넣어주면 INSERT SQL이 자동으로 날아간다. → save되고 난 후의 User는 id가 들어 있다유저 정보 조회하기 (SELECT)public List<UserResponse> getUserList(){ return userRepository.findAll().stream() .map(user -> new UserResponse(user.getId(), user.getName(), user.getAge())) .collect(Collectors.toList()); }findAll() : 해당 테이블의 모든 데이터를 조회한다.user를 stream으로 mapping시켜주고 user에 new UserResponse로 id, name, age를 넣어준다.그 후 결과를 List로 반환유저 정보 조회 후 수정하기 (UPDATE) public void updateUser(UserUpdateRequest request) { User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); userRepository.save(user); }findById를 사용하면 id를 기준으로 1개의 데이터를 가져온다..orElseThrow(IllegalArgumentException::new);→ Optional의 orElseThrow를 사용해 User가 없다면 예외를 던진다. 있을경우 user에 담긴다user.updateName(request.getName()); userRepository.save(user);도메인 user의 updateName메소드에 변경할 reqest.getName을 인자로 넣어 이름을 변경해주고save를 통해 user의 정보를 저장한다. (자동으로 UPDATE SQL이 날라가게 된다.) 어떻게 SQL을 작성하지 않아도 동작하지? JPA인가?→ Spring Data JPA가 도와준다.: 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리 By앞에 들어갈 수 있는 구절 정리find : 1건을 가져온다. 반환타입은 객체가 될수도 있고, Optional<타입>이 될 수도 있다.finalAll : 쿼리의 결과물이 N개인 경우 사용. List<타입> 반환exists : 쿼리 결과가 존재하는지 확인. 반환 타입은 booleancount : SQL의 결과 개수 반환타입은 longBy뒤에 들어갈 수 있는 구절 정리GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이에SELECT * FROM user WHERE age BETWEEN ? AND ?;List<User> findAllByAgeBetween(int startAge, int endAge);StartsWith : ~로 시작하는EndsWith : ~로 끝나는트랜잭션 이란 : 쪼갤 수 없는 업무 최소 단위→ 모든 SQL을 성공시키거나 하나라도 실패하면 모두 실패시키자!트랜잭션 시작하기start transaction;트랜잭션 정상 종료하기 (SQL 반영)commit;트랜잭션 실패처리(SQL 미반영)rollback; 트랜잭션 적용과 영속성 컨텍스트우리가 원하는 것은서비스 메소드가 시작할 때 트랜잭션이 시작되어서비스 메소드 로직이 모두 정상적으로 성공하면 commit 되고서비스 메소드 로직 실행 도중 문제가 생기면 rollback 되는 것@Transactional 어노테이션으로 우리가 원하는 것을 할 수 있다!SELECT 쿼리만 사용할경우, readOnly 옵션을 사용하여 데이터 변경을 위한 기능이 빠져 약간의 성능 향상이 있다. 영속성 컨텍스트란?테이블과 매핑된 Entity 객체를 관리/보관하는 역할⇒ 스피링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.변경 감지 (Dirty Check): 영속성 컨텐스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라고, 변경을 감지해 자동으로 저장된다.@Transactional public void updateUser(UserUpdateRequest request) { User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); //userRepository.save(user); }유저의 정보가 업데이트가 되었네? 바뀌었구나? 하고 자동으로 저장된다. 따라서 save메소드를 작성해주지 않아도 된다.쓰기 지연: DB의 INSERT / UPDATE / DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번만 날린다.예시로 유저가 저장되는 부분이 여러개 일때, 하나씩 날려서 저장하는것이 아니라 우선 기억해두고 한번에 저장하여 DB에 보내게 된다. 1차 캐싱: ID를 기준으로 Entity를 기억한다.쓰기 지연과 비슷하게 이전에 캐싱된 객체를 기억하고 있다가 동일한 값이면 DB에 계속해서 요청하지 않고 알려준다.책 생성 API 개발하기create table book (id bigint auto_increment, name varchar(255), primary key(id));name varchar(255)을 사용한 이유?@Column의 length 기본값이 255문자열 필드는 최적화를 해야 하는 경우가 아닐 때 조금 여유롭게 설정하는 것이 좋다.회고4번째과제는 과일가게 판매에 관련된 내용이었다. 삼단분리 후 뭔가 직접 구현해봐야하는 문제라 그런지 사실 과제를 하면서 재밌게 느껴졌다. 단순히 요구사항에 나와있는데로만 작성하다보니 id를 왜 long 타입으로 써야했을까? 등 '왜' 라는 생각을 잘 하지 못했는데 앞으로는 왜? 의문점을 가지고 생각해봐야겠다!!5번째 과제에선 클린코드에 나와있는 내용을 바탕으로 더 좋은 코드로 변경해보는 과제이었다.문제를 보고 우선 각각의 기능을 하는 함수로 나누어 분리해야겠다는 생각이 들었다. 또 if-else문을 지양해야한다고 했지만, 너무 많은 조건을 가지고 있는 if-else문의 경우에는 사용을 하는것이 오히려 나을수도 있다는 블로그 글을 참고하여조건부분을 따로 함수로 만들어 그 함수를 호출하는 if-else문을 생각하고 1차 리팩토링 하였다.하지만 그래도 if-else문에는 너무 반복되는 부분이 많았고 이부분을 결국 for문으로 변경하였다..!금요일에 깜짝 라이브를 참석하지 못해서 너무 아쉬웠지만 따로 내용을 올려주셔서 참고할 수 있었다 : )

백엔드워밍업BESpring

90년대 컴퓨터 공학 이야기 (3) — Trade-off

입학 전의 공학?중학교 때는 남학생들은 “기술”이라는 과목이 있었고, 고등학교 때 선택과목으로 “공업”이라는 게 있었다. 다니던 고등학교는 공업 대신 “상업”을 가르쳤던 학교였기에 대학 입학 전의 공업에 대한 이미지는 스크류드라이버+못질과 납땜을 해서 만들어 보았던 AM라디오키트 정도이겠다. 레고는 기억이 없고, 지금보다 훨씬 덜 화려했던 과학상자를 만졌던 기억도 있겠고, 용돈이 생기면 아카데미 프라모델을 만들며 놀았던 어린 기억들이 있다.고등학교까지의 엔지니어링검갈빨주노초파보회백 저항값을 읽는 정도가 선행학습이었고, 하드웨어의 아주 기초를 접했다면 접한 거라 할 수도 있겠다. 컴퓨터 과를 들어왔지만, 공학이라는 게 뭔지에 대한 깊은 고민을 한 것도 아니었고, 단지 자연대보다 남녀 성비가 좀 더 치우쳐져 있는 곳이라는 정도, 그 와중에 컴퓨터공학은 10% 의 여학우들이 있어 신기해 했으며, 남중남고를 나온 나에게는 누나들이 생겼다는 것도 신선한 충격이었다.Trade-off어린 병아리 시절, 1학년에 전공 과목은 ‘컴퓨터 공학 개론’ 이라고 하면서 C 언어를 배우는 과목이라고 했다. 학교에 오면 컴퓨터를 만질 수 있었고, 터보C 같은 걸로 숙제를 해서 디스켓으로 제출하면 된다고 하셨다.지도교수님이신 학과 2회 졸업생이셨던 민상렬 교수님을 잊을 수 없고, 기억 속에 여러 부분의 지분이 있으신데, 첫번째 에피소드가 이 trade-off 라는 단어이다. 중간에 하이픈이 있는 게 맞는 건지 없는 게 맞는 건지도 모르겠지만… 엔지니어로 평생 필요하게 될 근본적인 개념의 단어를 몇 개 알려 주마… 라고 하시면서 알려 주신 첫번째 단어였다. 모범생은 아니었고, 수업 시간에 다른 내용들이 많아서 단어들을 많이 알려 주시진 않았지만, 이 첫 단어의 임팩트가 강하게 남아 있다.인터넷 사전이 없이 영한사전을 뒤져가며 익히던 시절의 나에게는 신조어였고, 이 세상에 공짜는 없다는 것, 공학이 과학과 다른 점에서 중요한 개념이라는 것, 수학과 비교가 앞으로 왜 계속 필요한 지 등을 알게 되었고, 아이러니하게 회사 생활 하면서 결정을 내리는 일들이 많아지게 되면서 계속 되뇌어지는 단어이다.이후 알고리즘 시간에 시간 공간 복잡도를 다루면서 열심히 쓰이고, 이후 논문이라는 것들을 읽게 되면서 세상을 이해하는 데 많은 쓰임이 있었고, 지금도 많은 깨우침이 여기서 나온다 하겠다. 지금의 나에게 누군가가 엔지니어링이란 무엇인가 라고 묻거나 혹은 단어 하나만 고르라면 이 단어를 주저없이 이야기할 것이다.작년에 비전공자들이 전공자들을 이해하고자 할 때 필요한 내용들을 가지고 온라인 강의를 만들어 보고자 하는 게 있어(https://fastcampus.co.kr/dev_online_newcomputer) 작은 역할을 맡았는데, 그 때 처음으로 맴돌았던 키워드가 이것이었고, 이것을 시작으로 코스를 구성할 수 있었다. 

교양90년대컴퓨터공학

HYERIN JO

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

인프런 워밍업 클럽 2주차 발자국스터디 첫 모임에서 예산 계산기 앱 코드리뷰를 했는데 확실히 다른 사람들 코드를 보면서 배우는 것들이 있는 것 같다. 강의 요약리액트란?React 는 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리이다React는 인터렉션이 많은 웹 앱을 개발하기 위해서 주로 사용된다Framework vs LibraryFramework는 어떠한 앱을 만들기 위해 필요한 대부분의 것을 갖고 있다Library는 어떠한 특정 기능을 모듈화 해놓은 것이다리액트를 사용하는 이유?상대적으로 배우기 쉬운 라이브러리여러 기능들을 위해 이미 만들어져 있는 라이브러리 환경이 많음많은 기업들의 사용으로 검증이 된 라이브러리컴포넌트?리액트로 만들어진 앱을 이루는 최소한의 단위브라우저가 그려지는 원리와 가상돔리액트의 주요 특징 중 하나는 가상돔을 사용한다는 것이다Critical Rendering PathDom tree 생성Render tree 생성LayoutPaint가상돔이 작동되는 방식?리액트에서는 항상 렌더링 이전의 객체(가상돔)와 렌더링 이후의 객체(가상돔)를 가지고 있다리액트에서는 어떠한 State(데이터)가 바뀌면 가상돔이 새로 생성된다Diffing: 바뀐 부분을 찾는 과정Reconciliation: 바뀐 부분만 실제 돔에 적용시켜주는 것SPA웹 사이트의 전체 페이지를 하나의 페이지에 담아 동적으로 화면을 바꿔가며 표현하는 것JSX자바스크립트의 확장 문법리액트에서는 JSX를 이용해서 화면에서 UI가 보이는 모습을 나타내준다Key 속성?key는 React가 변경, 추가 또는 제거된 항목을 식별하는데 도움이 된다key를 이용해서 어떠한 부분이 바뀌었는지 인식할 수 있다Array.prototype.map배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환하는 메서드Array.prototype.filter주어진 배열의 일부에 대한 얕은 복사본을 생성하고, 주어진 배열에서 제공된 함수에 의해 구현된 테스트를 통과한 요소로만 필터링 한다state컴포넌트의 렌더링 결과물에 영향을 주는 데이터를 갖고 있는 객체Spread Operator(`...`)ES6에 새롭게 추가되었으며, 특정 객체 또는 배열의 값을 다른 객체, 배열로 복제하거나 옮길 때 사용한다React HooksReactConf 2018에서 발표된 class 없이 state를 사용할 수 있는 기능필요한 이유?Class Component로 사용되어온 React에서 느껴왔던 불편함이나 문제점들을 해결하기 위해 개발됨Propsproperties의 줄임말props는 상속하는 부모 컴포넌트로부터 자녀 컴포넌트에 데이터 등을 전달하는 방법props는 읽기 전용(immutable)으로 자녀 컴포넌트 입장에서는 변하지 않는다구조 분해 할당배열이나 객체의 속성을 해체하며 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식스스로 칭찬하고 싶은 점평일 낮에는 시간이 거의 없음에도 불구하고 금요일 스터디에 참여하기 위해 어떻게든 예산 계산기 앱 을 완성시켰다.아쉬웠던 점2주차 발자국을 제때 작성해서 올리지 못한 것과 주말에 시간이 있음에도 밀린 강의를 듣는 거에 시간을 많이 투자하지 못했다.다음주 목표발자국 마감기한에 맞춰 올리기!포켓몬 도감 앱 완성하기!

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

전다윤

[인프런 워밍업 스터디 클럽 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 21시간 전
최강현

[인프런 워밍업 스터디 클럽 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주차 회고

두번째 발자국 인프런 워밍업 클럽 스터디에 참여하여 일주일을 보낸 후 쓰는 두번째 회고록. Section 3 - 역할의 분리와 스프링 컨테이너스프링 컨테이너 : 서버가 시작되면 스프링 서버 내부에 만들어지는 거대한 컨테이너이 컨테이너 안에는 (여러) 클래스가 들어가게 된다.스프링 컨테이너 안에 들어간 클래스를 스프링 빈이라 한다.스프링 빈은 (미리 작성된 프레임워크의 코드에 의해) 서버가 시작될 때 자동으로 등록되기도 하고, 코더가 @RestController 등의 어노테이션을 이용해 직접 설정하기도 한다.이때 스프링 빈 간 필요한 의존성이 자동으로 설정되고, 인스턴스화도 이루어진다. 제어의 역전(IOC, Inversion of Control) 객체를 생성하는 과정에서 스프링 컨테이너가 필요한 의존성을 자동으로 선택해서 주입해주는 방식new를 이용해 어떤 구현체를 사용할지 결정하지 않고, 스프링 컨테이너가 대신 결정해주는 방식의존성 주입(DI, Dependency Injection) 컨테이너가 특정 구현체를 선택해 클래스에 넣어주는 과정 (필요한 의존성을 설정해주는 과정)스프링 빈 등록 방법@RestController, @Service, @Repository@Configuration(클래스에 붙임), @Bean(메소드에 붙여 메소드에서 반환된 객체를 등록)@Component(컨트롤러, 서비스, 레포지토리 외의 클래스를 추가적으로 등록하기 위해 사용)스프링 빈 주입받는 방법생성자를 이용해 주입(권장)Setter와 @Autowired 사용필드에 직접 주입(@Autowired 사용)@Qualifier Section 4 - 생애 최초 JPA 사용하기JPA(Java Persistence API)  자바 진영 ORM 기술 표준ORM(Object-Relational Mapping) : 데이터와 관계형 DB의 테이블을 짝짓는 방법(객체와 관계형 DB를 짝지어) 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙영속성 : 서버가 재시작되도 데이터는 영구적으로 저장되는 속성HibernateJPA를 구현해서 코드로 작성한 구현체JPA는 단순히 API이므로, 실제 동작을 위해서는 JPA 구현체가 필요함내부적으로 JDBC를 사용 JPA 어노테이션@Entity : 스프링이 객체와 테이블을 같은 것으로 바라봄Entity : 관리되어야 하는 데이터@Id : 해당 필드를 primary key로 간주@GeneratedValue : primary key는 자동 생성되는 값 @Column : 객체의 필드와 Table의 필드를 매핑JPA를 사용하려면 반드시 기본 생성자가 필요함!JPA 기능save : 주어지는 객체를 저장하거나 업데이트findAll : 주어지는 객체가 매핑된 테이블의 모든 데이터를 가져옴findById : id를 기준으로 특정한 1개의 데이터 가져옴Spring Data JPA : 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리By 앞에 들어갈 수 있는 구절 정리find: 1건을 가져옴. 반환 타입은 객체가 될 수도 있고 Optional<타입>이 될 수도 있음findAll: 쿼리의 결과물이 N개인 경우 사용. List<타입> 반환exists: 쿼리 결과가 존재하는지 확인. 반환 타입은 booleancount : SQL의 결과 개수를 셈. 반환 타입은 longBy 뒤에 들어갈 수 있는 기능 정리And / Or GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이에StartsWith : ~로 시작하는EndsWith : ~로 끝나는 트랜잭션(Transaction)쪼갤 수 없는 업무의 최소 단위 트랜잭션의 모든 로직이 성공적으로 수행되면 commit 처리되고, 예외가 발생하면 rollback 처리됨영속성 : 모든 로직을 성공시키거나, 로직들 중 하나라도 실패하면 모두 없었던 일로 하는 속트랜잭션으로 처리할 메서드 앞에 @Transactional을 붙여서 구현   영속성 컨텍스트(Persistence Context)테이블과 매핑된 엔티티 객체를 관리/보관하는 역할스프링에서는 트랜잭션 수행 중 엔티티 객체를 관리/보관하는 역할을 수행트랜잭션이 수행될 때 자동으로 생성되고, 트랜잭션이 종료되면 함께 종료됨영속성 컨텍스트의 특수 기능 : 변경 감지(Dirty Check) : 영속성 컨텍스트 안에서 불러와진 엔티티는 별도의 save 필요 없이 엔티티의 변경 사항을 자동으로 감지하여 저장쓰기 지연 : 모든 SQL 요청을 트랜잭션이 commit될 때 모아서 한 번만 날림1차 캐싱 : 조회된 엔티티 객체를 id를 기준으로 캐싱하여 DB 접근 횟수를 줄임   Section 5 - 책 요구사항 구현하기 책 생성 API HTTP Method : POSTHTTP Path : /bookHTTP Body (JSON) :  결과 반환 : X (HTTP 200 OK)대출 기능 HTTP Method: POSTHTTP Path: /book/loanHTTP Body(JSON) : 결과 반환 X : (HTTP 200 OK)반납 기능 HTTP Method: PUTHTTP Path: /book/returnHTTP Body(JSON) : 결과 반환 X : (HTTP 200 OK)  회고저번 주 강의는 기본적인 지식들을 쌓는 것이었는데, 이번주는 본격적인 내용으로 들어가면서 필기할 내용도 많아지고 배운 것들을 이해하는 데 더 많은 시간을 쏟아야 했다. 그래서 강의를 듣는 것도 좀 밀리고, 강의를 다 듣긴 했지만 배운 내용을 온전히 소화하지는 못했다. 다음주는 이번주 강의 내용을 다시 정리해보고, 시간을 좀 더 효율적으로 사용할 수 있도록 해야겠다. 이번주 금요일에 코드 리팩토링에 관한 라이브 방송이 있었는데, 그걸 모르고 있다가 방송이 끝나갈 쯤에야 참여해서 아쉬움이 컸다. 과제 5를 수행하면서 클린 코드를 짜는 것이 생각보다 고려할 점이 많다는 것을 느껴서 더더욱 그랬다. 다행히 키워드와 코드를 남겨주셔서 혼자 공부해봐야 할 것 같다.

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

이용수

[인프런 워밍업 클럽 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주차 강의를 듣고 회고록을 작성하면서 앞으로도 지금처럼 성실함을 중점으로 꾸준하게 강의를 듣고 기록하는 것이 중요하다고 생각했다. 다음주는 벌써 배포에 대해 배우는 시점이 온다. 개인적으로 제일 관심이 있던 부분이기 때문에 더 확실하게 이해하기 위해 주말에 미리 선행을 진행 해보려고 한다.

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