블로그
전체 32024. 05. 19.
0
[인프런 워밍업 클럽 1기 BE] 세번째 발자국
이번 주도 강의를 많이 듣지는 못했지만, 미니프로젝트 1단계를 개발하고, 중간점검에 참여하면서 많은 것을 느낄 수 있었습니다.강의는 26강부터 30강까지 들었습니다. 학습 내용 요약:26강: Spring Data JPA를 이용해 다양한 쿼리 작성하기25강에서 Spring Data JPA가 무엇인지에 대해 배웠습니다. 복잡한 JPA코드를 스프링과 쉽게 사용할 수 있도록 도와주는 라이브러리였는데요. 26강에서는 실제로 Spring Data JPA를 활용해 다양한 쿼리를 작성해보았습니다.Spring Data JPA의 가장 편리한 점은, 메서드 이름만 작성하면 필요한 SQL을 자동으로 조립해 준다는 점이었습니다.따라서, 메서드 이름을 어떻게 짓는지 규칙을 정확하게 알아야 합니다.find: 1개의 데이터만 가져옵니다.By 뒤에 오는 필드 이름을 기준으로 Where문을 작성합니다.By 앞에 올 수 있는 구절find: 해당 조건을 만족하는 객체 하나를 가져옵니다. 반환 타입은 객체가 될 수도 있고, Optional이 될 수도 있습니다.Optional 객체는 Java8 이후로 등장한, null을 감싸는 Wrapper 객체로, 값이 없을 수 있는 경우를 표현하기 위해 사용합니다. null로부터 안전성을 보장받을 수 있어 좋지만, 결국 Unboxing하고, Boxing 하는 과정이 오버헤드로 작용할 수 있습니다.따라서 null이 아님이 확실하다면 굳이 사용할 필요가 없습니다.제한된 경우에 null 대신 반환형으로서 사용하도록 설계되었다고 합니다.orElseThrow() 메서드를 사용해, 값이 없는 경우 예외처리를 할 수 있습니다.ex.orElseThrow(IllegalArgumentException::new)reference: https://mangkyu.tistory.com/70조회 연산에서는 값이 없는 경우가 자주 발생할 수 있기 때문에, null을 리턴하도록 하면 에러가 너무 많이 발생할 것입니다. 따라서 Optional 형태로 값을 리턴하는 것 같습니다. findAll: 쿼리의 결과물이 N개인 경우 사용합니다. List을 반환합니다.exists: 쿼리 결과가 존재하는지 확인합니다. 반환 타입은 boolean입니다.count: SQL의 결과 개수를 셉니다. 반환 타입은 long입니다.각 구절은 And나 Or로 조합할 수도 있습니다.ex. findAllByPriceLessThanEqualAndSoldBy 뒤에 들어갈 수 있는 구절:GreaterThan: 초과GreaterThanEqual: 이상LessThan: 미만LessThanEqual: 이하Between: 사이에StartsWith: ~로 시작하는EndsWith: ~로 끝나는 SQL을 자동으로 생성해주므로, 매우 편리하지만, 조건이 복잡해질수록 메서드 이름이 길고 복잡해져 조건을 간략하게 쓰는 것이 필요해 보였습니다.27강: 트랜잭션 이론편트랜잭션: 쪼갤 수 없는 업무의 최소 단위(All or Nothing)ex. 쇼핑몰에서 결제 실패가 발생하면, 주문 전체가 취소되는데 이는 트랜잭션으로 묶여있기 때문이다.start transaction(트랜잭션 시작) / commit(반영) / rollback(미반영) 28강: 트랜잭션 적용과 영속성 컨텍스트@Transactional 어노테이션으로 트랜잭션을 적용할 수 있습니다.이때, 조회(SELECT) 쿼리만 사용한다면, readOnly 옵션을 통해 성능을 향상시킬 수 있습니다.readOnly 옵션을 사용하면 dirty-read(트랜잭션 중에, 나중에 시작한 다른 트랜잭션이 먼저 완료되어, 결과가 다르게 보이는 것) 처리를 해줄 필요가 없으므로 성능이 향상되고, 영속성 컨텍스트도 readOnly 객체의 변경을 저장해줄 필요가 없으므로, 부담이 줄어듭니다.태현님께서도, readOnly 옵션을 true로 설정하는 경우, CRUD를 모두 제공하는 DB가 아니라 Read만 가능한 DB에 바로 접근하므로, 성능 향상이 있다고 말씀하셨습니다.하지만, 서비스 레이어에서 무분별하게 readOnly를 사용할 경우 성능이 저하될 수 있다는 실험도 있었습니다.reference: https://medium.com/@jkha7371/is-transactional-readonly-true-a-silver-bullet-1dbf130c97f8위 글에서 다른 흥미로운 내용도 많았는데, readOnly = true 처리를 해주더라도 이는 힌트일 뿐이며, 일부 트랜잭션 매니저는 힌트를 받지 못해 write 작업이 가능하고, 예외도 발생하지 않는다고 합니다. 영속성 컨텍스트의 능력 4가지1. 변경 감지(Dirty Check)영속성 컨텍스트 안에서 불러온 Entity는 명시적으로 save하지 않더라도, 변경을 감지해 자동으로 저장됩니다.ex. 강의에서 update() 메서드의 경우 save() 메서드를 호출하지 않아도 영속성 컨텍스트를 통해 자동으로 저장되었습니다.2. 쓰기 지연INSERT, UPDATE, DELETE를 바로 날리는 것이 아니라, commit될 때 모아서 한 번에 날림 -> DB와 통신 횟수가 줄어듦 DB야 미안해... 상황이 덜 발생할 수 있습니다! 3. 1차 캐싱ID를 기준으로 Entity를 기억하기 때문에, 캐싱된 객체는 완전히 동일합니다.(인스턴스의 주소도 동일)4. 지연 로딩일종의 Lazy Evaluation인 것 같습니다.연관관계가 있는 엔티티를 조회 시 한 번에 가져오는 것이 아니고, 필요 시 가져옴!reference: https://devoong2.tistory.com/entry/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8Persistence-Context%EC%9D%98-5%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%A7%9530강 책 생성 API 개발하기기존에 배운 내용을 바탕으로 Book 객체를 생성하고, 테이블 설계 -> 도메인, 리포지토리 -> 서비스 -> 컨트롤러, DTO를 작성해보았습니다.혼자 작성하는 과정에 테이블 필드 이름과 DTO의 필드 이름이 달라서, Spring Data JPA가 필드를 인식하지 못하는 문제가 있었습니다.왜 책 이름을 255자나 할당하는지 궁금했는데, 문자열을 타이트하게 잡았을 때, 나중에 조건에 변화가 생기면 테이블을 수정해야 하는데, 그게 쉽지가 않다고 하셨습니다.또, @Column의 length가 기본 255자기 때문에, 255로 설정하면, 옵션에서 length 조건을 지울 수 있습니다. ## 스터디에 임하는 자세:다른 일로 바쁠때는 스프링 공부에 소홀해지기도 하는데, 스터디 과제 마감이 있기 때문에 다시 스프링 공부를 하게 됩니다. 또, Domain / Repository / Service / Controller / DTO 로 구분하여 개발하는 과정이 번거로울 때도 있지만, 과제를 해나갈수록 익숙해지고 있습니다. 중간 점검 시간에 태현님께서 코드 리뷰를 해주셨는데, 코드를 그 자리에서 보시면서 어떤 선택지가 있고, 각 선택지의 장단점은 무엇이며, 본인은 어떤 선택지를 선호하고, 그 이유가 무엇인지에 대해 설명해주셨습니다. 사실 너무 잘하셔서 충격이었고, 저게 현직자구나... 저런 사항들을 고려하면서 코드를 작성한다면, AI에 쉽게 대체되지 않겠다는 생각도 함께 들었습니다. 코드 리뷰에 참여하신 찬영님도 정말 열심히 고민해서 코드를 작성해주셨고, 덕분에 깊이 있는 코드리뷰가 되었던 것 같습니다. 요즘 테스트에 관심이 많이 생겼는데, 중간점검 때 알려주신 테스팅 방법을 좀 더 찾아보고, 미니 프로젝트에도 녹여봐야겠습니다.
2024. 05. 12.
0
[인프런 워밍업 클럽 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를 적용해봐야겠습니다.
2024. 05. 05.
0
[인프런 워밍업 클럽 1기 BE] 첫번째 발자국
이번 학기 취준을 시작하면서 자바를 공부하기 시작했습니다. 시간이 된다면 스프링도 공부해봐야겠다는 가벼운 마음으로 스터디를 신청했는데, 자바 + 스프링이 만만한 친구들이 아니어서 마음을 고쳐먹었습니다. 학습 내용 요약:Section 0: 스프링으로 프로젝트를 시작하기 위한 환경 세팅 방법을 배웁니다.1. 환경 세팅 과정멘토님(태현님)께서 정말 상세하게 알려주시기 때문에 환경 설정에 큰 어려움은 없었습니다.다만, 저는 MySQL을 이전에 설치했던 적이 있어서 설치 과정에 오류를 겪었습니다. (M1 에어 기준)다른 분들께 도움이 되실까 싶어 해결 방법을 남깁니다. "[MySQL] Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)" 이 오류를 겪었는데요, MySQL 서버와 소켓으로 통신이 잘 안 된다는 얘기입니다.저는 /opt/homebrew/var/sql에 이전 데이터가 남아있어서 충돌이 발생했던 것 같습니다.해결 순서는1) brew uninstall mysql로 기존 MySQL을 삭제2) rm -rf /opt/homebrew/var/mysql로 남은 파일을 제거3) brew install mysql로 MySQL을 재설치4) 설치 후, cd /opt/homebrew/Cellar/mysql/{mysql 버전}/support-files (경로 이동)5) ./mysql.server start로 서버를 실행입니다.출처: [https://velog.io/@shyuuuuni/MySQL-Cant-connect-to-local-MySQL-server-through-socket-tmpmysql.sock-2-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-%EC%82%AC%EB%A1%80] Section 1:생애 최초 API 만들기(GET, POST API)스프링 부트를 처음부터 시작하는 방법과 서버 개발의 기본이 되는 네트워크 기본 지식, 그리고 GET, POST API를 작성하는 방법을 배웠습니다.1. 라이브러리 vs. 프레임워크- 기존에 라이브러리가 작은 범위, 프레임워크는 좀 더 큰 범위구나 생각하고 있었는데, 강의에서"라이브러리는 미리 만들어진 기능을 (클래스, 메서드 등) 가져다 사용하는 것이고, 프레임워크는 다 준비된 코드에 필요한 정보만 넣어 사용하는 것이다. 즉, 라이브러리를 활용해 프로그램을 작성하는 것은, 김치를 사서 김치찌개를 만드는 것이고, 프레임워크를 활용해 프로그램을 만드는 것은, 원데이 클래스에 가서 선생님이 시키는 대로 김치찌개를 만드는 것이다."는 얘기를 듣고 차이점이 이해되었습니다. 예를 들면, java.lang '패키지'의 System.out.println을 사용하는 것이지, java.lang 프레임워크의 println을 쓰는 게 아닙니다. 2. GET, POST API- 기존에 파이썬으로 몇 번 서버를 작성한 적이 있어 할만하다고 생각했는데, 자바와 스프링으로 API를 구현하면서 에러를 많이 만났습니다.1) private 필드를 사용해야 했는데, getter가 없어서 406 에러가 발생2) 파라미터를 받는 생성자만 정의해두고, 기본 생성자는 정의하지 않아서, 배열을 정상적으로 읽어오지 못하는 문제를 만났습니다. 그리고, 멘토님께서 주신 과제를 수행하면서 스프링보다 자바가 부족하다는 것을 느꼈습니다. 자바 공부를 더 열심히 해야겠습니다. Section 2:생애 최초 Database 조작하기JDBC 템플릿을 활용해 DB를 직접 조작해보았습니다. 파이썬으로 DB를 조작하는 것과 비슷해서 이해가 잘 됐습니다.1. 에러 핸들링업데이트, 삭제 API의 경우 값이 업데이트 되지 않았거나, 삭제되지 않은 경우에도 Status 200을 하지 않도록 에러 핸들링을 한 부분이 기억에 남습니다. 2. 람다식과 익명 클래스 차이람다식과 익명 클래스의 차이를 정리하다 보니, 그리 만만한 주제가 아니라는 생각이 들었습니다. 오라클에서 제공하는 자바 튜토리얼에는 내포 클래스(Nested Classes)라는 주제로 Local Classes부터 설명이 되어 있습니다. Local Classes -> Anonymous Classes -> Lambda Expressions 순으로 코드가 간단해지고, 담을 수 있는 정보의 양도 줄어듭니다. 하지만, 세 경우 모두 새로운 클래스를 만드는 것보다 코드가 간단해진다는 공통점이 있습니다.출처: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html Section 3:역할의 분리와 스프링 컨테이너좋은 코드는 왜 중요한지, Controller를 Controller / Service / Repository 로 3단 분리하는 과정에 대해 배우고 스프링 컨테이너에 대한 기본 지식을 배웠습니다. 1. 좋은 코드(Clean Code)와 역할의 분리사용자 요청을 통해 들어온 HTTP 바디 및 헤더를 처리하는 Controller부분과, DB 서버와 통신하며 값을 저장, 조회, 수정, 삭제하는 Repository, Controller와 Repository를 이어주는 Service까지 역할에 따라 기능이 분리되면서, 코드가 더 간단해지는 것을 볼 수 있었습니다. 2. 스프링 컨테이너 & 스프링 빈스프링 서버를 시작하면 컨테이너라는 클래스 저장소를 시작하게 되는데, 이 안에는 스프링 빈이라고 하는 클래스들이 등록되고, 스프링 컨테이너가 스프링 빈 간의 의존성을 관리해준다는 내용을 배웠습니다.그런데, 한 가지 궁금한 점이 있었습니다. 서버가 시작되고 스프링 컨테이너가 시작된다는데, 왜 컨테이너 안에 서버가 들어가는 게 아니고, 서버 안에 컨테이너가 들어가는지 궁금했습니다. 스프링 컨테이너와 도커 컨테이너가 헷갈려서 검색해보았는데, 둘은 이름은 같지만 아예 다른 개념입니다. 스프링에서 얘기하는 컨테이너는 주로 자바의 객체의 라이프사이클을 관리하는 기능을 합니다. 한편, 도커의 컨테이너는 실행 환경을 가상화하는 방법으로 이름만 같습니다. 그러니까 컨테이너 안에 서버가 들어간다고 할 때는, 도커의 컨테이너를 이야기 하는 것이고, 스프링 서버가 시작돼서 스프링 컨테이너가 시작했다고 하는 것은 서버 내부에 자바 객체를 관리하는 컨테이너가 시작했다는 이야기입니다. 3. IoC and DI수업 시간에 스프링 빈을 이용하면, 사용자는 코드의 변경이 거의 없이 스프링 컨테이너가 필요한 스프링 빈을 찾아서 인스턴스화한다는 내용을 배웠고, 이러한 방식을 IoC(Inversion of Control, 제어의 역전), DI(Dependency Injection, 의존성 주입) 라는 개념을 배웠습니다. IoC, DI 개념이 조금 덜 와 닿았는데, 아래의 글을 읽으면서 조금 이해가 되었습니다. https://velog.io/@ohzzi/Spring-DIIoC-IoC-DI-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0 글의 내용을 요약하자면 DI는 "패턴"이고, IoC는 프로그램의 제어권을 역전시키는 "개념"입니다. IoC를 구현하기 위해 DI를 사용할 수 있다.로 정리하는 것이 좋은 것 같습니다. 수업 시간에 배운 생성자를 사용하는 방식(Constructor Injection, 스프링 권장), Setter를 사용하는 방식(Setter Injection), 인터페이스를 주입하는 방식(Interface Injection)이 있습니다. 이 중 생성자와 Setter를 이용하는 방식은 수업 때 다루었지만, 인터페이스를 이용하는 방식은 처음 보았습니다. 하지만 세 가지 방식 모두 A라는 객체의 의존성을 외부에서 만들어 넣는(Dependency를 Injection) 하는 방식입니다.IoC, DI를 활용하면 코드가 더 깔끔해지고, 클래스의 테스트가 쉬워진다고 하는데, 테스트 를 공부한 후에, 이게 어떤 의미인지 직접 확인해봐야겠습니다. 스터디에 임하는 자세:스프링을 공부한지 이제 1주차이지만, 스프링이 간단한 프레임워크가 아니라는 것이 느껴졌습니다. 강의를 들으면 들을수록, 스터디 이후에도 계속 공부해야 겠구나 잘하려면 더 많은 시간을 들여야겠다는 생각이 들었습니다. 그리고 자바를 아직 너무 몰라서, 학교 수업 시간에 배운 내용 뿐만 아니라, 프로그램도 더 많이 작성해보고, 알고리즘 문제도 많이 풀어봐야겠습니다.