블로그
전체 3#카테고리
- 백엔드
#태그
- 백엔드
- 워밍업클럽
- 발자국
2024. 05. 19.
0
[인프런 워밍업 클럽 1기/BE] 세번째 발자국
3주차 발자국을 찍으며..목표를 세우는 것을 좋아하는데, 그 목표가 무너질 때마다 조금씩 힘이 들었던 것 같다.매 정해진 날짜의 강의만큼은 다 들어야지 생각을 했었는데, 이번주는 여러 일이 겹치며 다 듣지 못해 너무 아쉽고 완주조건인 100%를 언제까지 채웠어야 하는 지는 모르겠으나 어떻게든 짬을 내 최선을 다 해 마무리 완강하고자 한다.완강 한 이후 이 발자국에 수정하여 적거나, 다음주차 발자국이라도 찍어서 완성했다는 뿌듯함을 꼭 느끼고 싶다.그래도 배포 전 개념적으로 배워야 하는 수업까지는 완료했기 때문에 큰 걱정은 덜은 것 같다.git이나 데이터베이스, 리눅스 서버, 배포에 대해서는 어느정도 알고 해보았기 때문에 수업을 이전보다는 어렵지 않게 마무리 할 수 있을 것으로 예상한다.수업 마무리 열심히 듣고 마지막으로 다음주까지 미니 프로젝트에 대한 과제도 있는데, 미니 프로젝트를 그동안 열심히 배운 것을 활용해서 재밌게 최대한 좋게 잘 만들어 보려고 한다.섹션5. 책 요구사항 구현하기조금 더 복잡한 기능을 API로 구성하기. 30~32강섹션5. 책 요구사항 구현하기책 생성, 대출, 반납 API를 온전히 개발하며 지금까지 다루었던 모든 개념을 실습해본다.객체지향적으로 설계하기 위한 연관관계를 이해하고, 연관관계의 다양한 옵션에 대해 이해한다.JPA에서 연관관계를 매핑하는 방법을 이해하고, 연관관계를 사용해 개발할 때와 사용하지 않고 개발할 때의 차이점을 이해한다. [책 생성 API 개발하기]HTTP Method : POSTHTTP Path : /bookHTTP Body(JSON){ "name" : String // 책 이름 }결과 반환 X (HTTP 상태 200 OK이면 충분하다)① book 테이블 생성create table book ( id bigint auto_increment, name varchar(255), primary key (id) ); JPA @column의 length 기본값이 255라서 테이블까지 255로 맞추면 @column 생략 가능, 문자열 필드는 최적화를 해야하는 경우가 아니면 조금 여유롭게 설정하는 것이 좋다.컬럼 뒤에 not null을 써주면 null을 넣을 수 없음② Book 객체 생성 ③ BookRepository 생성④ DTO 생성⑤ Controller와 Service 생성, 위와 같은 순서로 완성해나갔다. 비슷한 방식으로 나머지 기능을 완성해나간다. [대출기능 개발하기]사용자가 책을 빌릴 수 있다. 다른 사람이 그 책을 진작 빌렸다면, 빌릴 수 없다.API 스펙HTTP Method : POSTHTTP Path : /book/loan결과 반환 X (HTTP 상태 200 OK이면 충분하다)HTTP Body(JSON) { "userName" : String, "bookName" : String }⇒ 이것만으론 대출 기록을 저장할 수 없다.(User/Book 테이블만 가지고는), 새 테이블 필요user_loan_history 테이블 생성 + domain 생성boolean으로 객체를 처리하면 DB의 tinyint에 잘 매핑된다!(0-false, 1-true)⑥ DTO / Controller / Service 구현[반납기능 개발하기]요구사항 : 사용자가 책을 반납할 수 있다.API 스펙HTTP Method : PUTHTTP Path : /book/return결과 반환 X (HTTP 상태 200 OK이면 충분하다)HTTP Body(JSON) { "userName" : String, "bookName" : String }테이블은 건드릴 것이 없고 DTO / Controller / Service 구현하면 될 것 같음반납 / 대출 API 스펙이 완전히 동일하다.이런 경우, DTO를 새로 만드는게 좋을까? 아니면 재활용하는게 좋을까?강사님은 새로 만드는 것을 선호, 나도 그렇게 생각하는 편이다.그래야 두 기능 중 한 기능에 변화가 생겼을 때 유연하고 side-effect 없이 대처할 수 있기 때문반납 기능까지 구현완료 여기서 고민할만한 내용이 있다.Java 언어는 객체지향적 언어이고, 대규모 웹 어플리케이션을 다룰 때에도 절차지향적인 설계보다 객체지향적인 설계가 좋다!"20강에서 스프링 컨테이너를 왜 쓸까?" 를 살펴보았던 이유 역시 보다 객체지향적인 설계를 하기 위한 맥락에서 출발했다.지금 코드를 조금 더 객체지향적으로 만들 수 없을까??User/UserLoanHistory가 직접 협업할 수 있게 처리할 수 없을까? 객체지향과 JPA 연관관계. 33~36강[33. 조금 더 객체지향적으로 개발할 수 없을까?]대출 기능 개선 방향⇒ 위의 것을 아래와 같이 바꿀 수 있겠다현재 반납 기능 상황⇒ 아래와 같이 개선할 수 있겠다.이것들의 선행조건 : User와 UserLoanHistory가 서로를 알아야 한다.N : 1 관계학생 여러명이 교실에 들어갈 수 있다. 학생N : 교실1@ManyToOne(UserLoanHistory 클래스에 있는 User에), @OneToMany(User 클래스에 있는 UserLoanHistory에) 어노테이션 붙여주기연관관계의 주인Table을 보았을 때 누가 관계의 주도권을 가지고 있는가UserLoanHistory가 User를 가리키고 있음(주도권이 있음)연관관계의 주인이 아닌 쪽에 mappedBy 옵션을 달아주어야 한다.연관관계의 주인의 값이 설정되어야만 진정한 데이터가 저장된다.[34. 연관관계]1:1 관계ex) 사람 : 실거주 주소서로 Entity에 포함되어 있음(연결되어 있음)DB 테이블은 한 쪽만 상대의 id를 가지고 있어도 연결시킬 수 있다.person이 address의 id를 가지고 있다고 가정⇒ person이 연관관계의 주인.이 때도 주인이 아닌 쪽에 주인 Entity의 필드명을 mappedBy를 붙여줘야 한다.연관관계의 주인 효과객체가 연결되는 기준이 된다.주인 Entity에서 setter를 해주지 않으면 DB 상에서 연결이 되어있지 않게 된다.결론상대 테이블을 참조하고 있으면 연관관계의 주인연관관계의 주인이 아니면 mappedBy를 사용연관관계의 주인의 setter가 사용되어야만 DB 테이블이 연결됨연관관계 사용 시 주의사항트랜잭션이 끝나지 않았을 때, 한쪽만 연결해둔 상태에선 반대쪽에선(반대 객체에선) 값을 알 수 없다.해결법setter를 한 번에 둘을 같이 이어주자주인 쪽의 setter에서 반대 객체의 setter를 불러 자신을 넣어주면 된다.N:1 관계@ManyToOne과 @OneToMany연관관계의 주인은 항상 N쪽임@ManyToOne은 단방향으로 사용할 수도 있다. (User Entity에서 mappedBy가 붙은 것을 삭제해도 정상동작한다)@JoinColumn연관관계의 주인이 활용할 수 있는 어노테이션(주인쪽에만 붙임)필드의 이름이나 null 여부, 유일성 여부, 업데이트 여부 등을 지정N:M 관계(@ManyToMany)ex) 학생과 동아리의 관계구조가 복잡하고, 테이블이 직관적으로 매핑되지 않아 사용하지 않는 것을 추천이 경우 ManyToOne으로 풀어헤칠 수 있음1:N 관계를 두 개 연결시켜놓은 것처럼 풀어나가는 방식을 추천cascade 옵션한 객체가 저장되거나 삭제될 때, 그 변경이 폭포처럼 흘러 연결되어 있는 객체도 함께 저장되거나 삭제되는 기능@OneToMany 쪽에(주인이 아닌 쪽에) 옵션으로 cascade = CascadeType.ALL 을 주면 된다.ex) user가 지워질 때 user_loan_history 쪽 데이터도 함께 지워진다.orphanRemoval 옵션이 옵션 없이 객체 내에서 user에서 user_loan_history 리스트의 하나를 지운다고해도 DB에는 영향이 가지 않는다!이 옵션도 주인이 아닌 User쪽에서 @OneToMany 쪽에 옵션으로 orphanRemoval = true 를 주면 된다.객체 간의 관계가 끊어진 데이터를 자동으로 제거하는 옵션[35. 책 대출/반납 기능 리팩토링과 지연 로딩]* 위의 그림을 아래와 같이 변경해보자BookService와 UserLoanHistory와의 관계를 없애고 User와 UserLoanHistory가 협업하게 변경하자도메인 계층에 있는 User와 UserLoanHistory가 직접적으로 협력하게 바뀐 것을 가리켜 도메인 계층에 비즈니스 로직이 들어갔다 라고 표현하기도 함변경작업@Transactional public void returnBook(BookReturnRequest request) { User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalArgumentException::new); // UserLoanHistory history = userLoanHistoryRepository.findByUserIdAndBookName( // user.getId(), request.getBookName()) // .orElseThrow(IllegalArgumentException::new); // history.doReturn(); // 위의 코드를 쓰지 않으므로 // 유저만 가지고 데이터를 처리할 수 있게됨 user.returnBook(request.getBookName()); 영속성 컨텍스트의 4번째 능력반납기능을 리팩토링하면서 발동되었다@OneToMany의 fetch 함수 default가 LAZY라 이렇게 됨옵션을 fetch = FetchType = EAGER로 변경하면 바로 다 가져오게 됨지연로딩은 영속성 컨텍스트가 있어야만 가능함.즉, 트랜잭션 내에서만 가능함지연로딩을 사용하게 되면, 연결되어 있는 객체를 꼭 필요한 순간에만 가져온다.연관관계를 사용하면 무엇이 좋을까?서비스 계층의 역할꼭 필요한 경우 서로 다른 도메인끼리 협업할 수 있도록 도와주고트랜잭션 관리외부 의존성(스프링 빈이 필요한 상황) 관리도메인의 역할도메인 객체가 표현하고 있는 비즈니스, 관심사에 대해서 로직을 처리 연관관계를 사용하는 것이 항상 좋을까? ⇒ 꼭 그렇지는 않다!지나치게 사용하면, 성능상의 문제가 생길 수도 있고 도메인 간의 복잡한 연결로 인해 시스템을 파악하기 어려워질 수 있다.또한, 너무 얽혀 있으면 A를 수정했을 때 B C D까지 영향이 퍼지게 된다.비즈니스 요구사항, 기술적인 요구사항, 도메인 아키텍처 등 여러 부분을 고민해서 연관관계 사용을 선택해야 한다.과제6. 과제#4에서 만들었던 API를 분리해보며, Controller-Service-Repository 계층에 익숙해져보기https://slime-feels-660.notion.site/8cf3f404a3bb43f6a0fe08637f0b27e6?pvs=46-1. 3계층 레이어로 분리하는 첫번째 문제는 과제#4를 할 때 이미 배운대로 분리해서 작성을 했다보니 엉겹결에 처리되었다.6-2. @Qualifier와 @Primary를 이용해 인터페이스 레포지토리를 구현한 2가지 레포지토리를 필요에 따라 적용되도록 배운대로 적용해보는 과제였다. 수업에 나온 내용대로 해보면 되는 것이었기 때문에 6-1이 이미 되어 있어 조금 빠르게 마무리할 수 있었다.과제7. 몇 가지 문제를 통해 JPA를 연습해 봅시다https://slime-feels-660.notion.site/60c761713fc348a8b963539f11eca3ac?pvs=47-1. 테이블 변경을 하고, JPA에 맞게 원래 작성했던 코드에서 수정하였다. (Entity에서는 카멜케이스로 작성, DB에서는 스네이크케이스로 작성하는 등..) 이유는 정확히 모르겠지만 @Query 어노테이션을 사용하면서 오류가 발생했었는데, 구글링 시 어노테이션 안에 오는 SQL은 JPQL을 사용하는 것이라는 것을 발견했고, 리턴타입으로 Entity가 아닌 것을 반환받으려면 Interface based Projection을 활용해야 한다는 글을 보고 이 인터페이스를 추가해 리턴타입으로 바꾼 후 추후에 Service단에서 원래 reponse타입으로 변경해주는 작업을 넣으니 결과가 잘 나왔었습니다. ※ 추후 더 공부해서 원리를 제대로 알고 쓸 수 있고 싶다는 생각을 했습니다.7-2. 2번은 Response만 결과에 맞게 생성하고 count만 하면 되어 간단하게 할 수 있었던 과제였던 것 같습니다.7-3. 1번과 마찬가지로 컬럼을 변경하고 DB와 연결하는 작업 등을 먼저 해주었습니다. 조금 작업이 필요한 과제였는데, 완성하고 나서 결과를 확인했을 때 되게 뿌듯한(?) 느낌이 들었습니다. 이걸로 미니 프로젝트를 제외한 모든 과제를 마무리했는데, 특히 마지막 과제를 하면서 API에 대해 어느정도 익숙해진 느낌이 들며 혼자 해냈다는 생각을 들게 해주어 보람찼습니다. 아마 과제를 내주신 강사님도 이런 느낌을 느끼길 바라 시며 내주신 것 같아서 이런 과제를 만들어주셔서 감사하다는 말씀을 드리고 싶었습니다.마무리3주가 진짜 순식간에 지나갔다. 이번주는 특히나 일이 겹쳐 강의도 계획대로 듣지 못했다.하지만 17일 특강도 듣고 강사님께 질문도 해보며, 나름대로 개발에 대한 생각이라고 해야할까 이런 게 많이 바뀌었다.하면 할수록 할 수 있는 것도 많아지는 것 같고 정말 재미있다고 생각한다.조금 남아있는 수업 또한 마무리해서 완강을 찍을 수 있도록 할 것이고.. 이렇게 앞으로도 공부할 것을 하나씩 작은 계획을 세우고 계획을 노력해 완성하고 그 계획을 완성했던 동안을 회고하며 이런 방식으로 또 공부할 수 있으리라는 생각을 하게 만들어주는 워밍업클럽이었다.비록 아쉬운 부분이야 있지만 지금처럼 회고를 하며 의지를 계속 다짐하다보면 잘해나갈 수 있지 않을까 기대해본다.
백엔드
・
백엔드
・
워밍업클럽
・
발자국
2024. 05. 12.
0
[인프런 워밍업 클럽 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 { }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주차엔 행복하게 발자국을 남길 수 있길 바래본다.
백엔드
・
백엔드
・
워밍업클럽
・
발자국
2024. 05. 05.
0
[인프런 워밍업 클럽 1기/BE] 첫번째 발자국
인프런 워밍업 클럽 1기 첫번째 발자국을 찍으며..이 스터디를 시작하게 된 동기는 직접 찾은 것은 아니고 친구의 추천으로 시작하게 되었다.공부를 해보지 않았던 분야는 아니지만, 성격상 동기부여 될 것이 있으면 좀 더 공부를 열심히 할 수 있을 것 같다는 생각이 들었던 것 같다. 그리고 마침 오래되어 까먹기도 많이 한 것 같아 다시 한 번 상기하면 좋을 것 같았다.이번 스터디로 공부에 대한 마음을 다시 한 번 잡고, 계획적으로 공부하는 습관을 기르기에 좋을 것 같다는 생각에 열심히 해보려 첫번째 발자국을 남긴다.섹션 1. 생애 최초 API 만들기어노테이션마법같은 일(설정 등)을 자동으로 해줌@SpringBootApplication : 스프링을 실행시킬 때 필요한 다양한 설정들을 자동으로 해줌서버컴퓨터가 특정 기능을 수행해준다.그래서 컴퓨터 자체를 서버라고도 함서버에 요청을 할 땐 인터넷(네트워크)으로 하게 됨네트워크현실세계에는 컴퓨터별 고유 주소(ip)가 존재인터넷을 통해 데이터를 주고 받음port : 특정 port를 사용하는 프로그램은 반드시 하나Domain name : ip주소는 외우기 어려우니 ‘이름’을 쓰는것 ⇒ 이러한 체계를 DNS(Domain Name System)이라함HTTP(TyperText Transfer Protocol)데이터를 주고 받는 표준. protocol : 표준/약속 GET : HTTP Method, 요청을 받는 컴퓨터에게 데이터를 달라고하는 것POST : 요청을 받는 컴퓨터에게 요청하는 행위, 원하는 자원을 적어줌. 실제 저장할 정보가 포함됨, 행위와 자원은 HTTP 요청 전 약속되어있어야함HTTP 요청을 받는 컴퓨터와 프로그램 정보 Http Method + path + Query(GET) OR Body(POST)PUT : 데이터를 수정하라(Body) / DELETE : 데이터 삭제하라(Query)HTTP 요청 문법(첫줄) 메소드 패스 쿼리 + HTTP 버전(다음 줄) 헤더(여러줄 가능)(한줄띄고 그다음 줄) 바디(여러줄 가능)HTTP 응답정상 처리되었다면 200 OK(상태코드)를 보낸쪽에 보냄응답에는 추가 정보(바디)를 담을 수도 있음300 - 다른 곳으로 옮겨라, 404 - 요청한 것찾을 수 없다, 500 - 내부에 문제가 생겼음요청에 대한 응답을 제공(serve)한 컴퓨터가 바로 서버요청을 한 컴퓨터가 Client(고객)문법(첫줄) 상태코드(여러줄) - 헤더한 줄 띄기(여러 줄) - 바디API(Application Programming Interface : 규칙)정해진 약속을 하여 특정 기능을 수행하는 것클라이언트와 서버가 HTTP를 주고 받으며 기능을 동작하는데 정해진 규칙을 API라 함URL(Uniform Resource Locator)주소창@RestController이 클래스를 API의 진입지점으로 만들어 줌그 안에 메소드를 만들어서 그걸 API가 사용하게끔 만들어줄 수 있음@GetMappingapi의 GET@RequestParam쿼리라고 명시주어지는 쿼리를 함수 파라미터에 넣음@RequestParam을 제거하고 클래스로 대체할 수 있음DTO(Data Transfer Object) 역할을 한 것임 POST API => HTTP Body를 이용한다. 이 때 사용되는 문법이 "JSON"JSON(JavaScriptBojectNotation) : 객체 표기법{ “name” : “최태현”, “age” : 99, “dogs” : [”코코”, “초코”], “house” : { ”address” : “서울” } } 한 Controller Class에 여러 API 추가 가능@PostMapping 어노테이션 사용@RequestBody : HTTP Body로 들어오는 JSON을 객체로 변경해준다.이 때 객체의 필드이름과 JSON key의 이름이 같아야 함 Controller에서 getter가 있는 객체를 반환하면 JSON이 된다.public class Fruit { public String getName() { return name; } public long getPrice() { return price; } }이게 가능한 이유는 @RestController가 붙어있기에 가능하다. 섹션2. 생애 최초 Database 조작하기테이블 만들기crate table [테이블 이름] ( [필드1 이름] [타입] [부가조건], [필드2 이름] [타입] [부가조건], ... primary key([필드이름]) );MySQL 타입 - 정수타입tinyint : 1바이트 정수int : 4바이트 정수bigint : 8바이트 정수실수타입double : 8바이트 정수decimal(A, B) : 소수점을 B개 가지고 있는 전체 A자리수 실수Decimal(4,2) = 12.23문자열 타입char(A) : A글자가 들어갈 수 있는 문자열varchar(A) : 최대 A 글자가 들어갈 수 있는 문자열날짜/시간 타입date : 날짜, yyyy-MM-ddtime : 시간, HH:mm:ssdatetime : 날짜와 시간을 합친 타입, yyyy-MM-dd HH:mm:ss위의 SQL이 DDL(Data Definition Language)아래는 DML(Data Manipulation Language)조회 Queryselect * from fruit where id = 1 and price select * from fruit where id = 1 or price select * from fruit where price BETWEEN 1000 AND 2000;select * from fruit where name IN ('사과', '수박');select * from fruit where name NOT IN ('사과', '수박');업데이트 Queryupdate fruit set price = 1500 where name = '사과';where 조건을 주지 않고 실행할 수 있으나, 모든 경우를 모두 변경할 수 있으니 조심해야 함삭제 Querydelete from fruit where name = '사과';where 조건을 붙이지 않으면 모든 데이터가 삭제됨. 주의필요우리의 스프링 서버가 MySQL DB에 접근하게 하자application.yml 파일에 DB 관련 설정을 적어서 가능private final JdbcTemplate jdbcTemplate; public UserController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }jdbcTemplate을 이용해 SQL을 쓸 수 있다. 생성자를 만들어 jdbcTemplate을 파라미터로 넣으면, 자동으로 들어간다.jdbcTemplate.update() 는 INSERT/UPDATE/DELETE 쿼리에 모두 사용할 수 있다.첫 파라미터로 sql을 받고, ?를 대신할 값을 차례로 넣으면 된다.return jdbcTemplate.query(sql, new RowMapper() { @Override public UserResponse mapRow(ResultSet rs, int rowNum) throws SQLException { long id = rs.getLong("id"); String name = rs.getString("name"); int age = rs.getInt("age"); return new UserResponse(id, name, age); } });RowMapper : SQL 쿼리결과를 안에 있는 타입으로 변경시켜주는 역할하는 함수람다를 사용해서 간단하게 변경할 수 있었음HTTP Method PUT/DELETEjdbcTemplate.update() 는 sql의 업데이트가 아니라 데이터에 변화가 있는걸 말함없는 유저를 업데이트/삭제하려 해도 200 OK가 나오는 게 문제!자바에선 Exception/Throw로 예외를 던져 처리할 수 있었음.스프링에서 매핑된 함수가 throw 에러를 하면 200 OK 대신 500 Internal Server Error가 나온다.⇒ 데이터 존재 여뷰를 확인하고 예외를 던지면 되겠다jdbcTemplate.query() 를 사용하면 반환값이 List로 감싸진다. 섹션3. 역할의 분리와 스프링 컨테이너Clean Code는 왜 중요한가?현업에서 코드를 읽고 조금 변경하거나 조금의 새 기능을 추가하는 일이 더 많다.그렇기에 코드를 읽는 것을 피할 수 없다. 잘 읽을 수 있도록 하는 능력을 키우는 것도 중요하다.내가 다른 사람의 코드를 읽듯이 다른 사람도 내 코드를 읽게되므로 나 또한 가독성이 좋은 코드를 작성하는 것도 중요하다.왜 한 Controller에서 모든 기능을 구현하면 안될까?함수는 최대한 작게 만들고 한 가지 일만 수행하는 것이 좋다.클래스는 작아야하며 하나의 책임만을 가져야 한다.반대된 다면 문제가 될 이유함수를 동시에 여러 명이 수정할 수 없다그 함수를 읽고 이해하는 것이 어렵다함수의 일부분을 수정하더라도 함수 전체에 영향을 미칠 수 있어, 수정을 함부로 하기 어렵다.너무 큰 기능이므로 테스트하기 어렵다.종합적인 유지보수성이 매우 떨어진다.Controller의 함수 1개가 하고 있던 역할API 진입지점현재 유저가 있는지 확인 후 예외처리SQL을 사용하여 DB와 통신Controller - API, HTTP 관련 역할 담당, Service를 사용Service - 분기 처리, 로직 담당, Repository를 사용Repository - DB와의 접근 담당이러한 것을 Layered Architecture라고 한다. ※ 트러블슈팅강의내용은 spring boot 2.x버전과 java 11사용으로 시작되었으나, 강의 시작 기준 spring initializr 기준 spring boot 3.x 버전과 java 17 버전을 사용할 수 밖에 없었음spring boot 3.2.5 + java 17 버전 연동(?) 문제(기존 java-home이 java 11이었고, spring boot 3.x 버전부턴 java 17을 꼭 사용해야 해서 발생한 오류)intellij 프로젝트 구조에서 java version을 변경하고, 시스템 환경변수에서 java home을 17버전으로 변경하여 해결mysql workbench safe mode 해제참고 : https://blog.naver.com/kkson50/221251167091@RequestBody에 매핑되는 DTO는 왜 빈 생성자가 필요한가참고 : https://innu3368.tistory.com/181 과제1. 어노테이션을 사용하는 이유(효과)는 무엇일까?https://slime-feels-660.notion.site/acb06609050b490a8d1de99c6395e8dd?pvs=4지금까지 어노테이션들을 너무 자연스럽게 쓰다보니 왜 쓰는지나 개념적인 부분을 생각하지 않고 너무 편해서 기능만 알고 쓴 것 같았는데 조사하다보니 단순 어노테이션이라는 키워드 하나에도 공부할 수 있는 부분이 상당히 많고 깊었다.앞으로도 단순히 사용법만 알고 기능을 사용하기보다 왜? 라는 것에 초점을 맞춰 공부해야 한다는 생각이 들었다.과제2. API 만들기https://slime-feels-660.notion.site/14c2b5efc21c46cd9ec46124704b31ea?pvs=42-1, 2-2 과제를 할 때는 생각보다 금방 했는데, 2-3번 과제를 할 때 다 만들어놓고 해결되지 않는 것을 이해를 못했다.구글링을 통해 결국 이유를 찾았지만 쉽다 생각한 것에도 또 무언가 있었다. 얕게 공부하기보다 깊게 공부할 수 있도록 해야겠다는 마음을 다시 먹게 만들어 준 과제같았다. 과제3. 자바의 람다식은 왜 등장했을까? 람다식과 익명 클래스는 어떤 관계가 있을까? 람다식의 문법은 어떻게 될까?https://slime-feels-660.notion.site/742bd212b32e4830b6fea014dc70c443?pvs=4람다식을 사실 많이 쓰진 않은 것 같다. 익숙한 걸 쓴다 해야할까 쓰면 코드량이 줄고 여러 기능들이 추가되지만 자연스럽게 코딩을 하다보면 편한 방법을 쓰게 된다. 추가적으로 스터디를 해서 내 익숙한 코딩방법을 좋은 방법으로 교정하는 작업이 필요하겠다는 생각을 하게했다.마무리1주를 돌아보며 생각보다 시간이 부족하다. 발자국을 위해, 복습을 위해 내용을 노션에 적으면서 수강을 하니 이 정도 시간이 들겠지 하고 계획한 것보다 시간을 더 쓰게 됐었다. 시간이 있는 날은 여유가 있네 생각하고 바로 쉬는 것보다 조금 더 예습을 해두는 게 공부하기로 계획한 것을 밀리지 않고 계속 해낼 수 있을 것 같아 그렇게 해보려 한다.아, 그리고 실수로 5/3 특강을 날짜를 착각해서 시간을 놓치게 되었는데 너무 아쉽고 17일날에는 꼭 알람을 해놓고 들어야겠다.공부를 하며, 강의를 들으며 더 깊게 공부하고 싶은 것들이 종종 보였는데, 그런 것들도 키워드를 모아 시간을 내 공부하여 궁금한 것들을 채워나가야겠다.
백엔드
・
백엔드
・
워밍업클럽
・
발자국