inblog logo
|
keepgoing
    SpringBoot

    [Springboot] 17 블로그 만들기 V3 시작

    JPARespository 적용해보자
    김호정's avatar
    김호정
    Sep 05, 2024
    [Springboot] 17 블로그 만들기 V3 시작
     
    💡
    JPARespository 사용할 예정
     
    ++
    V2 respository 에 @Transactional 삭제하기
     
    notion image
    notion image
    AOP는 여기 없어서 체킹을 못하고 직접 가져와야 한다!
     
    notion image
     
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    AOP 라이브러리 추가
     
    notion image
     
    application.properties
     
    -dev
    -prod
     
    파일 생성
     
    • yml도 쓰는데(가독성 때문에) 아무거나 써도 상관없음
     
    💡
    dev는 개발환경에 대한 설정파일
    prod는 서비스 할때 쓰는 설정파일
     
    예를 들어서 application.properites에
    ddl-auto : create 하면 초기화 되는데
    prod에서도 create로 하면 안되니까 dev, prod 로 나눠서 설정&사용함
     
    • prod 에는 id가 아닌 배포되는 os의 환경변수를 넣음
     
    notion image
     
    application.properties 에 dev를 active 하겠다고 설정해준다.
    → profies을 어떤걸 active 해줄까 ? dev !
     
    notion image
     
    # 1. UTF-8 server.servlet.encoding.charset=UTF-8 server.servlet.encoding.force=true # 2. H2 spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:mem:test spring.datasource.username=sa # 3. Hibernate spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=true spring.jpa.defer-datasource-initialization=true # 4. Dummy spring.sql.init.data-locations=classpath:db/data.sql # 5. Mustache Setting spring.mustache.servlet.expose-request-attributes=true spring.mustache.servlet.expose-session-attributes=true # 6. Query Format spring.jpa.properties.hibernate.format_sql=true # 7. OpenInView spring.jpa.open-in-view=false
    application-dev.properties 에 작성
     
    이제 -prod 를 만들자.
     
    • prod에서는 ddl-auto에 create로 하면 안됨.
    • spring.jpa.show-sql=true 하면 서비스할 때 로그가 찍히면 느려지니까 이것도 prod 환경에서는 지워줌.
    • spring.jpa.defer-datasource-initialization=true 더미데이터를 초기화 할 일이 없으니 이것도 필요없다.
     
    notion image
    mysql 디비 설정해줄때, my 라고 입력하면 2개가 뜨는데 cj가 최신꺼.
    → cj로 넣어준다.
     
    notion image
    # 1. UTF-8 server.servlet.encoding.charset=UTF-8 server.servlet.encoding.force=true # 2. H2 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/blogdb spring.datasource.username=root spring.datasource.password=1234 # 3. Hibernate spring.jpa.hibernate.ddl-auto=none # 4. Dummy # 5. Mustache Setting spring.mustache.servlet.expose-request-attributes=true spring.mustache.servlet.expose-session-attributes=true # 6. Query Format # 7. OpenInView spring.jpa.open-in-view=false
    application-prod.properties 완성
     
     
    여기까지 작성하고
     
    db 더미데이터 넣고, Board랑 User 만들기.
     
    notion image
     
    서버 돌려보면 dev profile가 잘 실행되고 있음
     
    notion image
    application.properties 에서 prod로 바꾸면 prod가 실행됨
     
    board, user, boardRequest, UserRequest 만들어줌
     
    ++
    팀장이 request, response 파일도 일단 만들어주기
    entity, core 도 만들어주기
     
    core 패키지 v2에서 고대로 복사해서 넣어주기.
    GlobalValidationHandler에 hello어노테이션 부분 안쓰니까 삭제하기.
     
    notion image
     
    UserRepository 를 interface로 만든다.
    → extends JPARepository 해주기
    • 인터페이스가 인터페이스를 상속할때는 extends
     
    notion image
     
    → JpaRepository를 상속하면 @Repository를 안붙여줘도 된다.
    interface는 new 할 수 없다.
    그래서 @Repository 붙이는게 의미가 없다.
     
    notion image
     
    Optional은 null을 들고 있는 선물박스.
     
    원래 값이 없으면 NoResultException이 터지는데,
    Optional 안에 값이 없으면 throw 날려서 404 터트리면 된다.
     
    ++
    모든걸 다 JPA 가 제공하는데 ( → 전체조회, PK조회, 삭제, 추가 등을 제공. )
    업데이트는 제공안해줌 → 더티체킹 하라고 함.
    → username으로 조회하기 같은건 우리가 직접 만들어야 함
     
    notion image
    JPA를 사용할 때는 이렇게 써주면 된다.
     
    ++
    이건 문법일뿐. 이해하려고 하지마
    이해하는건 Persistance context같은 개념
     
    이해하는건 개념이나 문법은 찾아쓰자
     
    ++
    복잡한 쿼리를 쓸때는 일반 Repository에 넣지 말고
    UserQueryRepository를 생성해서 넣어라!
    notion image
     
    복잡한 쿼리들은 여기다 쓰자!
     
    notion image
    UserService 만들고 2개를 di한다.
    → UserRepository와 UserQueryRepository !
     
    notion image
     
    복사해서
    notion image
     
    v3에 넣어주기
     
    기본세팅 완료!
     
    ++
     
    notion image
    매개변수로 들어오는 User와 조회해서 가져오는 User의 변수 명이 같으면
    안되고 또 헷갈리니까
    ⇒ 조회된거 뒤에는 PS를 붙임( → 영속성 컨텍스트에 있는 객체를 의미)
    User userPS = userRepository.findByUsername(user.getUsername());
     
    ++

    깃허브 Fork

    협업할 때 콜라보로 안하고 fork로 하는 방법이 있다.
     
    fork 뜨고 수정하고 PR 요청 → 깃허브로 모르는 사람이랑 협업?하는 법 !
     
    notion image
     
     
    notion image
     
    notion image
    포크뜨면 그 프로젝트가 복사된다.
     
    notion image
     
    → 내 repository에서 해당 이름으로 검색됨
     
    notion image
     
    Sync fork → 계속 동기화 됨
    Contribute → push 하고 contribute를 누르면 내 코드를 반영할 수 있다.
    ( 실제로는 반영요청하는 것 )
     
    위의 Spring-teacher는 업스트림(upstream)이고,
    내거는 오리진(Origin)이다.
     
    notion image
    Sync fork 를 눌러주고, Code 복사해서 로컬에 클론한다.
     
    git pull origin master 해주고
     
    내가 담당한 기능 구현한 뒤에
     
    push 해주면 된다. (master에 push)
     
    그 후
    notion image
     
    Contribute 눌러서 Pr 요청 하면 됨
     
     
    ++

    Optional 말고 객체로 받자!

    JpaRepository의 findById는 Optional로 return 하는데 반환되는 걸 XXXOP로 이름을 지어서 받는다.
     
    Optional<Board> boardOP = boardRepository.findById(boardId);
     
    근데 이렇게 받지 말고
    boardRepository.findById(boardId).orElseThrow(() → new Exception404(”게시글이 없습니다.”);
     
    이렇게 람다식으로 받으면서 throw 도 같이해주면
    Board 타입으로 받을 수 있다.
     
    💡
    Optional 로 받지 말고, 람다식으로 작성 후 orElseThrow 하고 Board 타입으로 받기 ! ( 변수명은 boardPS ) → 영속성 객체니까 Board boardPS = boardRepository.findById(boardId).orElseThrow(() → new Exception404(”게시글이 없습니다.”);
     
     
    Java에서 Optional 객체를 만드는 방법은 여러 가지가 있습니다. Optional은 Java 8에서 추가된 클래스이며, 이는 값이 있을 수도 있고 없을 수도 있는 컨테이너 역할을 합니다. Optional을 사용하면 null 값을 다룰 때 발생할 수 있는 NullPointerException을 방지할 수 있습니다.다음은 Optional 객체를 만드는 몇 가지 방법입니다.1. 빈 Optional 객체 생성빈 Optional 객체를 만들려면 Optional.empty()를 사용합니다.
    java Optional<String> emptyOptional = Optional.empty();
    2. 값이 있는 Optional 객체 생성값이 있는 Optional 객체를 만들려면 Optional.of()를 사용합니다. 이 메서드는 절대 null을 허용하지 않습니다. 만약 null을 전달하면 NullPointerException이 발생합니다.
    java Optional<String> optionalWithValue = Optional.of("Hello, World!");
    3. null이 될 수 있는 값을 가진 Optional 객체 생성null이 될 수 있는 값을 감싸는 Optional을 만들려면 Optional.ofNullable()을 사용합니다. 이 메서드는 null 값을 허용하며, 만약 전달된 값이 null이면 빈 Optional 객체를 반환합니다.
    java Optional<String> optionalWithNullableValue = Optional.ofNullable("Hello, World!"); Optional<String> optionalWithNull = Optional.ofNullable(null); // 빈 Optional 반환
    4. Optional 값 사용하기Optional 객체에서 값을 가져오는 방법은 여러 가지가 있습니다. 예를 들어, 값이 존재할 때만 특정 작업을 수행하려면 ifPresent() 메서드를 사용할 수 있습니다.
    java optionalWithValue.ifPresent(value -> System.out.println(value));
    값이 있으면 그 값을 반환하고, 없으면 기본값을 반환하려면 orElse() 메서드를 사용할 수 있습니다.
    java String value = optionalWithValue.orElse("Default Value");
    이와 같은 방법으로 Optional을 활용하면 null을 보다 안전하게 처리할 수 있습니다.
     
     
     
     
     
    ++
    OpenInView를 false로 하면
    외부에서 요청하면 트랜젝션이 종료될때 세션이 종료된다.
    아무것도 트랜젝션이 안걸려있으면 ( → 서비스에 트랜젝션이 1개도 없으면 )
    DB에 조회하고 세션이 바로 끊겨버린다.
     
    // 그림
     
    그냥 트랜젝션과 readOnly = true 트랜젝션이 있음
    클래스에 readOnly = true 트랜젝션 걸면 아래 매서드에 다 걸림 ( → 서비스 레이어 전체에 )
     
    트랜젝션을 걸어두면 고립성이 지켜짐.
     
    변경(추가, 삭제, 수정) / 조회(Readonly=true)가 동시에 들어가면
    → 조회는 커밋하기 전 데이터를 본다( → 기존의 개수 1개를 본다. )
    → 만약 조회에 트랜젝션이 안걸려있으면
     
    커밋되기전, 커밋되기 후 데이터가 다르다…
    JPA는 이걸 막아둠. → 그래야 팬텀데이터가 안생긴다.
     
    읽을때는 readonly true를 걸고 ( → 커밋되기 전의 데이터를 본다. )
    → @transactional (readOnly = true)를 서비스클래스 맨위에 붙여줌
     
    notion image
     
    → readonly true를 걸면 더티체킹을 안함 ( = 연산이 좀 줄어든다 )
    (원래 더티체킹을 하면 종료될때마다 뭐가 변경되었는지 변경감지를 하는데
    select 이니까 변경감지를 할 필요가 없음. 변경된게 없으니까 )
    → 변경(등록, 수정, 삭제)할때는 transacitonal을 붙임
    → 즉, 여기에 걸어두면 조회 매서드에 @Transactional(readOnly=true)가 걸리는거고
    변경 매서드에는 추가로 @Transactional 을 붙여주는 건가 ?
     
     
    ++
    “게시글 저장”을 구현하는데 로그인과 회원가입이 없다.
    → 그래서 임시 session을 생성+저장한 뒤에 ( → 강제 로그인 ) 저장 기능을 만들었다.
     
    이 임시로 만들어준 session.setAttribute는 나중에 다없애주어야 한다. ( 나중엔 로그인 기능이 있을테니 )
    → 나중에 다 수정해야 하는데 일일이 찾기가 어렵다
    → 그래서 주석을 달아주어야 한다.
    → 해당 코드 위에 // TODO: 로그인 기능이 완료되면 삭제 예정!! 이라고 주석을 달아준다.
     
    notion image
     
    그리고 나중에 shift 를 2번 클릭하고 , TODO 를 검색하면 TODO 주석 달아둔 곳이 다 뜬다.
    → 나중에 TODO 주석을 검색해서 한번에 다 수정한다.
    → 이렇게 안하면 나중에 못 고친다.
    → 그 외 문제 있는건 github 이슈에 올리기!
     
    notion image
     
     
    ++
     
    conflict 나서 내가 로컬에서 수정하고 push 했는데, 그전에 다른 사람들이 push 했으면 수정했음에도 불구하고 conflict 날 수 있다. → 이럴땐 다시 pull 받아서 로컬에서 conflict 해결하고 다시 올려야 함
     
    오늘 fork 해서 받은 프로젝트.
    나는 나대로 내 repository에 계속 업데이트해서 push 할 예정.
     
    ++

    Devtool은 왜 설정해주는가

    → devtools는 컴파일 시에 리로드 해줌.
    근데 인텔리제이에서 사용할땐 내부에서 추가로 설정을 해줘야 동작함.
    .class로 바뀐거 를 jar 파일(실행파일)로 패키징 되어야 함.
    내 컴퓨터에 컴파일된 파일이 있다고 실행되는게 아님.
    실행되면서 웹서버가 만들어져야 함.
    build라는 폴더 내부에 만들어져 실행됨.
    reload하면 deploy됨. 웹 서버에. jar 파일이.
     
    Share article

    keepgoing

    RSS·Powered by Inblog