ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JDBC Template을 이용한 회원정보 변경 내역 저장 (4)
    Spring 2023. 2. 25. 09:05

    이번에는 닉네임의 변경 이력을 저장하고 조회하는 기능을 만들어 보자.

     WriteService

    public Member create(RegisterMemberCommand command) {
        /*
            목표 - 회원정보(이메일, 닉네임, 생년월일)를 등록한다.
                - 닉네임은 10자를 넘길 수 없다.
            파라미터 - memberRegisterCommand
    
            val member = Member.of(memberRegisterCommand)
            memberRepository.save()
         */
    
        var member = Member.builder()
                .nickname(command.nickname())
                .email(command.email())
                .birthday(command.birthday())
                .build();
        var savedMember = memberRepository.save(member);
        saveNicknameHistory(savedMember);
        return savedMember;
    }
    
    public void changeNickname(Long memberId, String nickname) {
        /*
            1. 회원의 이름을 변경
            2. 변경 내역을 저장한다.
         */
        var member = memberRepository.findById(memberId).orElseThrow();
        member.changeNickname(nickname);
        memberRepository.save(member);
    
        saveNicknameHistory(member);
    }
    
    private void saveNicknameHistory(Member member) {
        var history = MemberNicknameHistory
                .builder()
                .memberId(member.getId())
                .nickname(member.getNickname())
                .build();
        memberNicknameHistoryRepository.save(history);
    }

     

    이전에 진행했던 닉네임을 변경하는 코드에서, 변경된 이름들을 따로 저장해 주는 코드를 만든다.

    또한 Member 객체가 생성되었을 때도 이력이 남아야하므로 create메서드에도 추가해준다.

    이전 게시글에서 Update에 대한 Test만 진행하고 실제로 update 되는 코드는 작성하지 않았으므로 추가해준다.

     

    private Member update(Member member) {
        var sql = String.format("UPDATE %s set email = :email, nickname = :nickname, birthday = :birthday WHERE id = :id", TABLE);
        SqlParameterSource params = new BeanPropertySqlParameterSource(member);
        namedParameterJdbcTemplate.update(sql, params);
        return member;
    }

     

    마찬가지로 controller도 추가로 만들어 준다.

     

    @PostMapping("/{id}/name")
    public MemberDto changeNickname(@PathVariable Long id, @RequestBody String nickname) {
        memberWriteService.changeNickname(id, nickname);
        return memberReadService.getMember(id);
    }

     


    NicknameHistory

    history를 저장하기 위해서는 별도의 table이 필요하므로 새롭게 정의해준다.

    create table sns.MemberNicknameHistory
    (
        id int auto_increment,
        memberId int not null,
        nickname varchar(20) not null,
        createdAt datetime not null,
        constraint memberNicknameHistory_id_uindex
            primary key (id)
    );

     

    @Getter
    public class MemberNicknameHistory {
        private final Long id;
        private final Long memberId;
        private final String nickname;
        private final LocalDateTime createdAt;
    
        @Builder
        public MemberNicknameHistory(Long id, Long memberId, String nickname, LocalDateTime createdAt) {
            this.id = id;
            this.memberId = Objects.requireNonNull(memberId);
            this.nickname = Objects.requireNonNull(nickname);
            this.createdAt = createdAt == null ? LocalDateTime.now() : createdAt;
        }
    }

    참고로 History 특성의 data들은 member 혹은 다른 테이블과 겹친다고 하더라도 중복이 아니다.

    따라서 history 특성 데이터는 정규화 대상이 아니다.

     

    생성된 테이블을 바탕으로 객체를 만들어주고 DTO도 생성해보자.

     

    DTO 

    public record MemberNicknameHistoryDto(
            Long id,
            Long memberId,
            String nickname,
            LocalDateTime createdAt
    ) {
    }

     

    또한 이제 저장을 할 수 있는 Repository를 만들자.

     

    Repository

    @Repository
    @RequiredArgsConstructor
    public class MemberNicknameHistoryRepository {
        final private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
        final static private String TABLE = "MemberNicknameHistory";
    
        static final RowMapper<MemberNicknameHistory> rowMapper = (ResultSet resultSet, int rowNum) -> MemberNicknameHistory
                .builder()
                .id(resultSet.getLong("id"))
                .memberId(resultSet.getLong("memberId"))
                .nickname(resultSet.getString("nickname"))
                .createdAt(resultSet.getObject("createdAt", LocalDateTime.class))
                .build();
    
        public List<MemberNicknameHistory> findAllByMemberId(Long memberId) {
            var sql = String.format("SELECT * FROM %s WHERE memberId = :memberId", TABLE);
            var params = new MapSqlParameterSource().addValue("memberId", memberId);
            return namedParameterJdbcTemplate.query(sql, params, rowMapper);
        }
    
        public MemberNicknameHistory save(MemberNicknameHistory member) {
            /*
                member id를 보고 갱신 또는 삽입을 정함
                반환값은 id를 담아서 반환한다.
             */
            if (member.getId() == null) {
                return insert(member);
            }
            throw new UnsupportedOperationException("MemberNicknameHistory는 갱신을 지원하지 않습니다.");
        }
    
        private MemberNicknameHistory insert(MemberNicknameHistory history) {
            SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(namedParameterJdbcTemplate.getJdbcTemplate())
                    .withTableName(TABLE)
                    .usingGeneratedKeyColumns("id");
    
            SqlParameterSource params = new BeanPropertySqlParameterSource(history);
            var id =  simpleJdbcInsert.executeAndReturnKey(params).longValue();
            return MemberNicknameHistory
                    .builder()
                    .id(id)
                    .memberId(history.getMemberId())
                    .nickname(history.getNickname())
                    .createdAt(history.getCreatedAt())
                    .build();
        }
    }

    기존에 만들었던 Repository와 비슷하므로 ctrl + c와 ctrl +v 를 해준다.

    또한 history에서는 수정이 없으므로 update 메서드를 지우고, save에서도 기존 update 대신에 Exception을 던져준다.

     

    ReadService

    이제 변경이력을 조회하는 기능을 만들어 보자.

    public List<MemberNicknameHistoryDto> getNicknameHistories(Long memberId) {
        return memberNicknameHistoryRepository
                .findAllByMemberId(memberId)
                .stream()
                .map(this::toDto)
                .toList();
    }
    
    private MemberNicknameHistoryDto toDto(MemberNicknameHistory history) {
        return new MemberNicknameHistoryDto(
                history.getId(),
                history.getMemberId(),
                history.getNickname(),
                history.getCreatedAt()
        );
    }

     

    여기저기 옮겨다니며 코딩 하다보니 순서가 뒤죽박죽이다...

    댓글

Designed by Tistory.