[인프런 워밍업 스터디 클럽 0기_BE] 2주차 회고록 정리

[인프런 워밍업 스터디 클럽 0기_BE] 2주차 회고록 정리

요일 별 내용 정리

Day6

 

강의

  • 스프링 컨테이너란?

    • 개발자 대신 객체의 생명 주기를 관리해주는 곳입니다.

  • 스프링 빈이란?

    • 스프링 컨테이너 안에 인스턴스를 넣어주는 것이 스프링 빈이다. 따라서 빈으로 등록된 메서드들은 스프링 컨테이너가
      생명 주기를 관리합니다.

    • 빈 등록 방법에는 @Bean을 붙혀줘야하며, 항상 클래스에 @Configuration을 선언해줘야 가능합니다.

      • 여기서 @Configuration 의 사용은 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할때 사용됩니다.

    • @Component를 사용하여 빈 등록이 가능합니다.

      • 주어진 클래스를 컴포넌트로 확인하며 해당 클래스는 서버가 런타임 시점에 자동으로 감지됩니다.

      • 강의를 공부하며 배웠던 @Repository,@Service,@RestController에 대한 어노테이션 내부에 @Component어노테이션이 달려있습니다. 주로 사용할 때는 개발자가 직접 작성한 클래스에 사용되며 @Bean어노테이션이 생략됩니다.

      image

여기서 더 나아가 @Configuration vs @Component

  • 다양한 빈 주입방식

    • 주입 방식 중에 자동으로 주입을 받는 어노테이션이 있는데 그건 바로 @Autowired입니다.

    • 자동으로 스프링에 의존성을 주입하는 역할을 수행합니다.

  • 생성자를 이용해 주입 받는 방식

     

    private final BookJpaRepository bookJpaRepository;
    private final UserLoanHistoryRepository userLoanHistoryRepository;
    private final UserRepository userRepository;

    @Autowired
    public BookService(BookJpaRepository bookJpaRepository, UserLoanHistoryRepository userLoanHistoryRepository, UserRepository userRepository) {
        this.bookJpaRepository = bookJpaRepository;
        this.userLoanHistoryRepository = userLoanHistoryRepository;
        this.userRepository = userRepository;
    }

생성자를 통해 주입 받는 방식으로 가장 권장하는 방법입니다. @RequiredArgsConstructor를 사용하여 생성자를 생략할 수 있습니다. 여기서 @RequiredArgsConstructor은 롬복이 정의해놓은 어노테이션으로 final이나 notnull로 되어있는 생성자에 대한 내용을 자동으로 생성해주는 어노테이션입니다.

  • Setter를 이용한 주입 방식

private JdbcTemplate jdbcTemplate;

@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate
}

Setter에 의존성 주입을 하는 방법이지만 사용하지 않습니다.

  • 필드에 직접적인 주입 방식

@Autowired
private JdbcTemplate jdbcTemplate;

필드에 직접적으로 의존성 주입을 직접거는 방식이지만, Field injection is not recommended이라는 프레임워크 메세지와 함께 사용되지않는 방법입니다.

image강의에 대한 일부 내용으로 인터페이스를 생성하여 다형성을 활용해 메서드를 나눠놓았지만, 문제점은 어떤 곳에 대한 클래스를 사용해야할지 모르는 상황이 올 수 있는데 두가지 방법이 있습니다.

 

  • @Primary

    • 클래스 위에 어노테이션을 사용하면 스프링 컨테이너에 있는 빈에 대한 우선권을 주는 어노테이션입니다.

  • @Qualifier

    • 많은 후보군 중에서 어떤 것을 스프링 컨테이너에 넣어주는 역할을 하는 어노테이션입니다.

    • 클래스에서 소문자로 줄여서 사용합니다. appleService 또한 각 클래스내에 @Qualifier(”지정한 이름”)를 사용하면 양쪽 모두 사용하면 @Qualifier끼리 연결이 된다.

@Primary, @Qualifier 우선 순위

  • @Qualifier를 직접 작성한 클래스에 대해 우선 순위를 가진다.

  • 스프링은 사용자가 직접 명시한것이 우선순위가 높다.

 

과제

노션 : https://silvercastle.notion.site/6-92b90a65eb4a4cfaa7241162fd8e3fa2?pvs=4

깃허브 : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/daliy/daysix

 

 

Day7

강의

  • JPA란?

    • JPA(Java Persistence API)는 자바 진영의 ORM의 표준 기술

  • ORM이란?

    • 객체와 관계형 데이터베이스를 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java진형 규칙

  • Hibernate란?

    • 자바 진영의 다양한 ORM 프레임워크 중 가장 많이 사용되는 프레임워크 Hibernate로 만들어진 ORM 표준 기술은 JPA

  • JPA를 사용하기 위한 설정

  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql:
        show_sql:
        dialect: org.hibernate.dialect.MySQL8Dialect
  • ddl-auto : 스프링이 시작할때 DB 테이블 동작을 진행합니다.

    • create : 컴파일 시작할때 기존 테이블이 있어도 삭제 후 새로생성

    • create-drop : 런타임 종료후 drop

    • update : 객체와 테이블이 다른 부분만 변경

    • validate : 객체와 테이블이 동일한지 확인

    • none : 아무 동작x


      create,update는 개발서버에서 사용하며, 운영서버에서는 사용하지 않고 none과 validate는 운영서버 및 개발서버에서 자주 사용할꺼같다 생각이 들었습니다.

    • show_sql: JPA에 대한 쿼리를 보여주는 설정

    • format_sql: 쿼리에 대한 동작에 정렬 설정

    • dialect: 데이터베이스에 따른 방언을 맞추기 위한 설정

       

       

  • JPA에 대한 다양한 어노테이션

     

    • @Entity : 테이블과 객체가 1:1로 대응하는 데이터를 이야기합니다.

      • 왜 기본 생성자가 없는것을 entity에 작성할까? JPA는 Reflection API를 사용하여 객체를 생성하기 때문에 기본 생성자가 필요 private은 지연로딩 설정시 Proxy 오류가 발생합니다.

        • Reflection API : 리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API

    • @Column

@Column(nullable = false , length = 20, name = "name")
  1. null확인, 길이, 이름을 지정합니다. 테이블의 컬럼을 지정하는 어노테이션입니다.

  2. name이 동일하다면 name은 생략하다. Column 어노테이션도 생략이 가능하다.

     

     

  • J

    PA의 쿼리 작성 방법

public interface UserRepository extends JpaRepository<User,Long> {
}
  1. 레포지토리 인터페이스를 생성하여 JpaRepository<엔티티 객체 ,엔티티 객체의 ID>를 입력해주면 Spring Data JPA 사용이 완료 됩니다.

    private final UserRepository userRepository;

    public void saveUser(UserCreateRequestDto request) {
        userRepository.save(new User(request.getName(), request.getAge()));
    }
  1. 생성자를 이용한 의존성 주입을 진행한 후 쿼리문을 날려 사용합니다. save는 이때 엔티티의 있는 필드명을 입력을 해줘야합니다.

기본적으로 제공하는 Spring JPA 메서드

  • save: 사용자를 생성,수정을 하는 메서드

  • findById: 아이디를 통해 가져오는 메서드 Optional<타입>, 타입

  • findAll: 전체 조회 반환은 List

  • exist : 쿼리 결과가 존재하는지 boolean

  • count : SQL의 개수를 센다.

Optional<타입>을 활용하여, null값에 대한 처리를 진행하여 코드에 대한 가독성 및 null처리를 유연하게 처리할 수 있습니다.

과제

해당 과제를 진행하면서 Stream문에 대한 사용방법과 도메인 객체를 만들면서 아 이땐 이렇게 했구나라는 대답을 얻을 수 있었습니다. 또한 왜 사용해야지 하면서 깊게깊게 공부하다보니 이런식으로 동작하는 구나에 대해 많이 배웠습니다.

또한 클래스와 엔드포인트가 동일한 문제를 겪으면서 프로젝트에 대한 패키지에 대해 다시 되돌아보는 시간이였습니다.

코치님이 말씀해주신 enum을 통해 분기처리를 작업하는 방향성에 대해 생각하지 못했는데 알게 되어 기뻤습니다.

노션 : https://silvercastle.notion.site/7-28ba1cb6c81a4972b6ec38a4cd69ef96?pvs=4

깃허브 : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/daliy/dayseven

 

 

Day 8

강의

  • 트랜잭션이란?

    • 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위 , 쪼갤 수 없는 업무의 최소 단위

    • 강의 내용 중에 쇼핑몰 사이트에 주문을 하기 위해 주문작업에 대한 내용을 쪼개보면, 주문기록저장,포인트저장,결제기록 저장을 진행해야하는데 주문기록저장,포인트저장 작업이 정상적으로 수행되어도 결제기록 저장에 예외가 발생하면 다같이 처리를 해주지 않아야하는데 주문기록저장,포인트저장은 저장이 되는 상황을 막기위해 쿼리동작을 한번에 처리하는 것을 의미하기도합니다.

  • SQL에서 사용하는 트랜잭션 사용방법

    • start transaction; : SQL에서 트랜잭션 시작

    • commit : 트랜잭션 실행

    • rollback: 트랜잭션 실행 실패

    image만약 다른 접속 사용자들이 insert문을 작업을 진행하였고, 또 다른 접속 사용자가 select를 해도 commit이 되어있지 않기 때문에 다른 접속자의 저장된 내용을 확인 할 수 없다.

     

  • 강의에서 트랜잭션 적용해보기

//어노테이션을 달면 메서드 시작 아래에서 트랜잭션 시작
//함수가 예외없이 잘끝나면 commit
//아니면 rollback;
    @Transactional
    public void saveUser(UserCreateRequestDto request) {
        userRepository.save(new User(request.getName(), request.getAge()));
        throw new IllegalArgumentException("틀렸습니다.");
    }

트랜잭션 어노테이션을 통해 saveUser라는 곳에 트랜잭션이 시작되고, save를 진행하여 커밋을 대기하는 상황에 예외가 발생하여 rollback이 되어 데이터베이스상에 저장이 되지 않는 모습을 볼 수 있습니다.

image

    @Transactional(readOnly = true)
    public List<UserResponseDto> getUsers() {
        List<User> users = userRepository.findAll();
        return users.stream()
                .map(user -> new UserResponseDto(user.getId(), user.getName(), user.getAge()))
                .collect(Collectors.toList());
    }

select절은 @Transactional의 속성 중 readonly = true는 생성,수정,삭제에 대한 처리를 진행하지않고 조회만 해 성능 향상에 도움이 됩니다!!

여기서 알아야할점은 checkedException은 예외가 발생하여도 트랜잭션 작업에 롤백 상황이 있어도 커밋이 됩니다.

하지만 구글링을 통해 확인 해보았는데! try-catch및 개발자가 직접적으로 예외를 던져준다면, checkedException도 롤백을 진행할 수 있습니다. 꼭 checkedException은 트랜잭션 작업에 롤백이 안된다는 무조건적인것이 아니였습니다!!

  • 영속성 컨텍스트란?

    • 테이블과 매핑된 Entity객체를 관리/보관하는 역할

    • 트랜잭션을 사용하면 영속성컨텍스트가 생성되고 , 트랜잭션을 종료하면 영속성컨텍스트가 종료된다.

    • Entity를 저장하거나 업데이트를 진행 하는 변경을 감지해 자동으로 저장된다. → 변경 감지

    • 쓰기지연 : INSERT/UPDATE/DELETE SQL 을 바로 날리는것이 아니라 commit이 진행되면 한번에 날려서 보낸다. 왜 한번만 날릴까? 한번에 묶어서 하면 성능이 올라간다

    • 1차캐싱 : id를 기준으로 entity를 기본적으로 사용할 수 있도록함

Day 9

강의

  • 책 생성 API 개발

    • 예전 강의에서 배운대로 클래스를 작성했고, 레이어드 아키텍쳐를 통해 Controller,Service,Repository에 패키지 구조를 나눴습니다.

    • 엔티티객체를 생성했고, DTO를 구간을 나눠 작성하는 방법과 Spring Data JPA를 통해 저장하는 방법을 복습했습니다.

  • 책 대출 기능 개발하기

    • user_loan_history라는 테이블을 생성하며, id값을 참조하고있고, tinyint를통해 boolean을 알아 성능에 대해 줄일수 있다는 점을 배웠습니다.

    • 기존과 동일하게 DTO , 계층과 JPA를 사용하여 기능을 개발했습니다.

  • 반납 기능 개발하기

    • 반납기능 강의중에 DTO를 재활용하는 것과 새로 만드는것에 대해 이야기가 나왔는데

    • 두 기능에 한 기능에 변화가 생기면 유연하고 사이드 이펙트에 대해 대처를 할 수 없다 생각했습니다.

    • 회사코드 및 개인 프로젝트에서는 하나로 묶은 만능 DTO를 만들었는데 각각 나눠서 관리하는게 더 낫다 생각하여 이후 작업에 진행할 예정입니다.

    저는 지금도 좋은 코드를 작성한 줄 알았고, 지금도 그런 방식으로 코드를 구현해왔지만 더 좋은 방법이 있다. 라는 내용과 함께 많이 설레기도 했습니다. 정말 강의를 들으면서 기본기와 심화적인 디테일내용을 잡을 수 있어 너무너무 좋았던 것 같습니다.

 

Day10

강의

  • 코드를 객체지향 적으로 짤 수 없는 강의를 들으며, 예전 코드 및 지금 코드에서 쿼리 저장할때 각각 저장하는 방식을 사용했는데, 도메인 계층을 하나의 관심사로 묶어 처리하는 과정을 배웠습니다. 예전에 코치님께 여쭤본 쿼리를 나눠서 보내는 과정에 대해 아! 이때 이런식으로 쿼리를 만들면 되겠구나. 라는 것을 깨달았습니다(아닐 수도 있습니다 하하..)


    또한 다양한 연관관계에 대해 배웠는데.

     

 @ManyToOne
 private User user;

JPA는 연관관계에 대해 사용하는 방법으로 @ManyToOne으로 묶어 사용했으며, 반대 방향에서는

@OneToMany(mappedBy = "user")
    private List<UserLoanHistory> userLoanHistoryList  = new ArrayList<>();

를 사용하여 양방향 연관관계에 대해 배웠습니다. 연관관계에 대한 주인은 주도권이 가지고있는 연결된 필드를 가진 사람이 주인이며mappedBy는 주인이 아닌 곳에 달아줘야하는것을 알았습니다. 또한 @ManyToOne를 가진 쪽이 주인인것으로 배웠으며, 연관관계에 많이 헷갈렸고 지금도 많이 헷갈렸지만, 조금씩 알아갔습니다.

또한 주인의 대한 setter를 사용해야 연관되어있는 엔티티에 값이 저장되며, 주인이 아닌곳에 저장시, 주인 엔티티가 저장이 되지 않는 것을 배웠습니다.

 

  • 다양한 연관관계 종류

     

    • @JoinColumn : 연관관계 주인이 활용할 수 있는 어노테이션

      필드의 이름이나 null여부 , 유일성 여부 , 업데이트 여부등을 지정

    • @OneToOne : 1대1로 연관관계를 맺는 방식입니다. 하나의 사용자는 하나의 집 주소를 가지고 있습니다!

    • @ManyToMany : 구조가 복잡하여 사용하지 않은것을 추천합니다. 중간 테이블을 만들어 관리를 진행해봤고 이것이 더 좋은 방법이라 생각이 됩니다!

    • cascade : 함께 연결되어 있는 객체도 함께 저장되거나 삭제 되는 기능!

      만약 user와 대출기록이 묶여있다면 데이터베이스에 user와 대출기록이 삭제된다.

    • orphanRemoval : 대출 기록이 삭제되어도 user는 삭제가 되지않는다. 객체간의 관계가 끊어진 데이터를 자동으로 제거하는 옵션

       

    public void loanBook(String bookName) {
        this.userLoanHistoryList.add(new UserLoanHistory(this, bookName));
    }

    public void returnBook(String bookName){
        UserLoanHistory targetHistory = this.userLoanHistoryList.stream()
                .filter(history -> history.getBookName().equals(bookName))
                .findFirst()
                .orElseThrow(IllegalArgumentException::new);
        targetHistory.doReturn();
    }

도메인 객체에 대한 관심사를 묶어 협력하여 도메인 계층에 비지니스로직이 들어간 코드를 보고 와.. 저렇게 할수도 있구나 이러면 기능적으로 명확해지겠구나라는 생각을 했고 연관관계에 대해 조금더 깊게 생각이 들었습니다.

그렇지만 연관관계에 대해 많이 묶여버린다면 사이드 이펙트에대한 고려와 성능상의 문제가 생길 수 있어 여러 아키텍쳐 공부 및 비지니스 요구사항에 대해 공부를 해야한다 느꼈으며, 아 아직 정말 많이 멀었다 생각했습니다. 그렇지만 해야할 일을 하나씩 진행하며 성장하고 있다는 기분을 받았고, 그것이 원동력을 만들어주었던 것 같습니다. 2주차에는 급하게 작성한 부분들때문에 부족해보이고 모자랐다 생각이 들었고, 다음 주차에서는 좀 더 신경을 많이 써서 진행을 해야겠다 생각했습니다.

또한 미니 프로젝트를 팀 별로 구성하여 작업을 진행해보며, 내가 미쳐 생각하지 못했던 부분, 내가 더 나은부분에 대해 토론을 하며 배워갈 예정입니다.

댓글을 작성해보세요.