항상 흘러가면 흘러가는 대로 살아왔지만 취준생으로서 내가 한달동안 어떻게 살았는지 꼼꼼히 되돌아보고 앞으로 미래를 설계하기 위해서 달단위 회고를 해보려 한다.
잔디깎이범 퇴출
대학생 때는 학생 방패라도 있었지 지금은 아니다. 내 안의 잔디깎이범을 몰아냈다. 이 과정에서 쓸데없는 알고리즘 한문제씩을 올리는 기염을 토하기도 했지만 5월 말의 공백 한칸은 정말 공부한 것만 올리자는 마음가짐을 먹은 날이였다. 열심히 살자;;
백기선님 웹 애플리케이션 강의 수강
나름 이전의 댓글 필터링 커뮤니티 프로젝트를 통해 스프링 MVC, 스프링 시큐리티, JPA를 헤딩하면서 나름 늘었다고 생각했었다. 하지만 구글링으로 막히면 해결하던 퀄리티와 달리 내가 고민했던 부분에 대한 베스트 프렉티스가 거의 이 강의에 담겨있다고 느꼈다.
Spring Security 유저 정보 변경 시 Authentication.Principal 초기화
이 강의에서는 Authentication Principal에 한번 DB를 갔다와서 ID가 존재하는 Detached Entity를 이용해 세션에서의 유저정보를 관리하고 EntityManager에게 Merge를 통해 DB까지 유저정보를 저장한다. 하지만 나는 프로젝트 전체적으로 DirtyChecking을 통해 DB 유저정보를 관리하고 싶었다. Security Context로부터 받은 Detached Entity는 Repository에서 유저 검색을 위해 Id만 사용했고 DB 유저정보를 변경후에 Security Context의 세션 유저정보를 초기화시켜주는 방법을 채택했다.
Detached Entity와 Merge를 사용하면 별도의 Security Context는 관리가 필요없지만 merge 시에 값 전체가 대체되기 때문에 Security Context에 Entity 정보를 모두 저장해야한다는 리스크가 있다.
내가 선택한 방법은 DirtyChecking으로 변경이 일어난 부분만 JPA가 알아서 바꿔줘서 데이터가 통으로 날라갈 일은 없지만 Security Context를 변경 시마다 초기화해줘야된다는 단점이 있다.
뷰 반환 컨트롤러 레이어 테스트
MockMVC를 통해 뷰를 반환해도 뷰의 이름, 모델의 attirbute들 체크, 리다이렉션 시에 URL등을 체크할 수 있는 것을 알 수 있었다.이전 댓글 필터링 프로젝트의 후기로 대문짝만하게 서버 사이드 렌더링 프로젝트라 컨트롤러 레이어 테스트를 하지 못했다는 망언을 바로 삭제할 수 있게된 계기였다.
OSIV를 통한 영속성 컨텍스트의 범위
컨트롤러 메소드에 트랜잭션이 안달렸지만 레포지토리나 서비스에서 읽어온 엔티티들을 지연로딩을 숨쉬듯 사용해온 입장으로서 가끔가다 기묘한 분위기를 느꼈는데 이는 OSIV(Open Session In VIew) 때문이였다. 기본값으로 켜져있으며 이 상태에서는 모두 영속성 컨텍스트의 범위이다. 트랜잭션 안에 있으면 수정도 가능하지만, 트랜잭션 밖의 컨트롤러 메소드에서도 영속성 엔티티를 통해 조회는 가능하다.
N+1 문제(EntityGraph) , (QueryDsl)
JDBC, MyBatis 등 얕은 Sql Mapper 경험만 하고 JPA를 다룬 대가중에 가장 크다고 할 수 있겠다. Sql Mapper를 통해 DB에서 데이터를 가져오면 발생하고 싶어도 발생할 수 없는 문제를 너무 추상화된 JPA를 바로 사용해서 이 문제점을 잘 모르고 더티체킹, 양방향 연관관계, 연관관계의 주인, 연관관계 편의메서드 이런쪽만 공부한 것 같다. 일단 JPA를 사용하고 쿼리를 사용할 때 가장 중요한 것은 서비스에서 필요한만큼의 정보를 가져와야한 다는 것이다. 너무 적어도, 너무 많아도 안된다.
@Entity
@Getter
@EqualsAndHashCode(of = "id")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Team {
@Id
@GeneratedValue
private Long id;
@ManyToMany
@Builder.Default
private Set<Account> managers = new HashSet<>();
@ManyToMany
@Builder.Default
private Set<Account> members = new HashSet<>();
@Column(unique = true)
private String path;
@ManyToMany
@Builder.Default
private Set<Tag> tags = new HashSet<>();
@ManyToMany
@Builder.Default
private Set<Zone> zones = new HashSet<>();
Team 엔티티의 대략적인 연관관계는 다음과 같다.
Team은 매니저가 멤버, 지역정보, 관심태그를 관리한다.
만약 지역정보를 추가하기 위해 Team 엔티티를 조회해온다면 접근하는 유저가 매니저인지 확인이 필요할 것이고, 팀의 지역정보도 가져와서 추가해야할 것이다. 이런 경우의 서비스 로직을 위해서 레포지토리, 엔티티 단에서는 매니저, 지역정보가 포함된 Team 정보를 넘겨줘야 한다. 만약 그냥 findById로 팀을 찾아온다면 매니저를 체크할 때 매니저를 조회하는 쿼리가 발생하고, 지역정보를 추가하기 위해 지역정보를 조회하는 쿼리가 발생할 것이다. 모든 연관관계 맵핑을 지연로딩으로 처리하는 것이 좋은 이유는 참조 엔티티에 접근할 수 있어서 좋은 것이 아니라 바로 내가 가져오고 싶은 정보 외에 것은 가져오지 않기위해 좋은 것이다.
스프링 이벤트, 비동기 멀티 쓰레드
패키지 의존성을 낮추고 멀티 쓰레드를 통해 느린 이메일 서비스를 사용자의 경험에서 삭제하는 과정을 배웠다. Kafka보다 매우 순한 맛이고 충분히 내가 프로젝트에 이용할정도로 유익한 것 같다.
Builder 잘쓰자
Builder가 파라미터 가독성을 좋게하고, 파라미터 타입이 같은 때에 안헷갈리게 해주는 장점을 가지지만 @AllArgsContruct와 함께 써버리면 이후 파라미터에 들어가지 않은 값은 기본값도 안들어간다. @Builder.Default나 필요한 생성자를 만들고 @Builder를 쓰자
6월 예고
프로젝트 만들기
배운것은 바로 써먹어야한다. 강의를 들으며 중간중간 내 입맛으로 바꾼 부분도 있고 미리 구현하고 강의 후에 수정했지만 조금 더 다듬기 위해 프로젝트를 하려고 한다.
공모전
6월 9일 장애인 앱 공모전 예선 결과가 나온다. 예선 통과하면 앱을 구현해야해서 만약 붙게되면 배운것을 여기다 써먹을 것 같다.
알고리즘
알고리즘을 하루에 최소 두문제는 풀고있는데 과연 늘기는하는걸까 하는 생각이 든다. 그래도 알고리즘을 잘하면 비즈니스 로직을 짤때 많은 도움이 되겠구나 하는걸 이번달 강의 들으면서 느꼈다. 열심히 늘려서 내가 원하는 서비스를 만드는 그날이 오길;;