본문 바로가기
💻 뚝딱뚝딱/북북클럽

[개발일지#007] 회원 등록/수정/조회 API 개발

by 뚜루리 2025. 1. 21.
728x90
320x100

[사용기술]

Java, Spring Boot, Spring JPA, MySQL

 

[만들려는 것]

책을 위한 SNS.

 

[오늘 하려는 것]

회원(Member) 등록/수정/조회 API 개발

 


 

MemeberApiController.java

1.회원가입

package seulgi.bookbookclub.api;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.dto.*;
import seulgi.bookbookclub.service.MemberService;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
public class MemberApiController {

    private final MemberService memberService;

    //회원가입
    @PostMapping
    public CreateMemberResponse saveMember(@RequestBody CreateMemberRequest request){
        Member member = new Member(request.getMemberId(), request.getName()
                                 , request.getNickName(), request.getInfo());
        Integer seq = memberService.join(member);
        return new CreateMemberResponse(seq);
    }

	///소스생략////

}
  • 엔티티를 직접 받아오지 않고 CreateMemberRquest 객체를 생성하여 한번 감싸서 가져오고, CreateMemberResponse 객체도 생성하여 보여줄 때도 한 번 감싸서 보여줌.
  • 왜 별도의 객체를 생성하여 감싸 가져오고 보여주나요? : 엔티티 객체를 그대로 사용하게 되면 엔티티가 변경될 때 난감해지고, 또 API의 스펙은 너무 다양한데 엔티티가 그걸 모두 수용할 수 없음.

 

CreateMemberRequest.java

package seulgi.bookbookclub.dto;


import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CreateMemberRequest {

    private String memberId;
    private String name;
    private String nickName;
    private String info;

}

 

CreateMemberResponse.java

package seulgi.bookbookclub.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CreateMemberResponse {

    private Integer memberSeq;

    public CreateMemberResponse(Integer memberSeq) {
        this.memberSeq = memberSeq;
    }
}

 

(+) MemberService.java 수정

package seulgi.bookbookclub.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.repository.MemberRepository;

import java.time.LocalDateTime;
import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;


    //회원가입
    @Transactional
    public Integer join(Member member){
        validateDuplicatieMember(member);
        memberRepository.save(member);
        return member.getMemberSeq();
    }

	//소스생략//
}
  • 회원 가입 후에 String 타입의 멤버 아이디를 반환했었는데 고유한 값을 반환하는 것이 맞다고 판단되어 시퀀스를 반환하는 것으로 변경하였음. 

 

 

회원등록(가입)API 테스트

포스트맨을 통해서 테스트하였음.

 

 


MemeberApiController.java

2. 회원수정

package seulgi.bookbookclub.api;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.dto.*;
import seulgi.bookbookclub.service.MemberService;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
public class MemberApiController {

    private final MemberService memberService;
	
    //소스생략//
    
    //회원가입
    @PostMapping
    public CreateMemberResponse saveMember(@RequestBody CreateMemberRequest request){
        Member member = new Member(request.getMemberId(), request.getName()
                                 , request.getNickName(), request.getInfo());
        Integer seq = memberService.join(member);
        return new CreateMemberResponse(seq);
    }

	//소스생략//

}
  • 회원 수정도 업데이트용 요청객체와 응답객체를 별도로 생성하였음.

 

UpdateMemberRequest.java

package seulgi.bookbookclub.dto;


import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class UpdateMemberRequest {

    private String password;
    private String nickName;
    private String info;

}

 

 

UpdateMemberResponse.java

package seulgi.bookbookclub.dto;


import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class UpdateMemberResponse {

    private Integer memberSeq;
    private String Nickname;
    private String info;

}

 

 

(+) Member.java 수정

package seulgi.bookbookclub.domain;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@Getter
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class Member {

    //코드생략//

    public Member(Member member, String password, String nickname, String info) {
        this.memberSeq = member.getMemberSeq();
        this.memberId = member.getMemberId();
        this.password = password;
        this.nickname = nickname;
        this.info = info;
    }


}
  • 생성자를 추가하였음.

 

(+) MemberRepository.java 수정

package seulgi.bookbookclub.repository;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import seulgi.bookbookclub.domain.Member;

import java.util.List;

@Repository
public class MemberRepository {

    @PersistenceContext
    private EntityManager em;

	//코드생략//

    public void update(Member member) {
        em.merge(member);
    }

	//코드생략//
}
  • 레파지토리에는 변경감지 기능을 이용하여 회원을 수정하는 메소드 생성.

 

 

(+) MemberService.java 수정

package seulgi.bookbookclub.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.repository.MemberRepository;

import java.time.LocalDateTime;
import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;

  	//코드 생략 //

    @Transactional
    public void updateMember(Integer memberSeq, String password, String nickName, String info) {
        // 기존 회원 조회
        Member existingMember = memberRepository.findByMemberSeq(memberSeq);
        if (existingMember == null) {
            throw new IllegalArgumentException("존재하지 않는 회원입니다.");
        }
        Member updatedMember = new Member(existingMember,
                password != null && !password.isBlank() ? password : existingMember.getPassword(),
                nickName != null && !nickName.isBlank() ? nickName : existingMember.getNickname(),
                info != null && !info.isBlank() ? nickName : existingMember.getInfo()
        );
        memberRepository.update(updatedMember);
    }
    //코드 생략 //
   
}
  • MemberService에도 회원 정보를 수정하는 메서드가 없어서 생성해 줬음.
  • 값이 들어있는 객체만 수정하게끔 하느라 조금 더 복잡해짐.

 

회원수정 API 테스트

 


MemeberApiController.java

3. 회원조회

package seulgi.bookbookclub.api;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import seulgi.bookbookclub.domain.Member;
import seulgi.bookbookclub.dto.*;
import seulgi.bookbookclub.service.MemberService;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class MemberApiController {

    private final MemberService memberService;

    //코드생략//
    
    @GetMapping("/members")
    public Result members(){
        List<Member> findMembers = memberService.findMembers();
        List<MemberDto> collect = findMembers.stream().map(m -> new MemberDto(m.getMemberId(), m.getNickname(), m.getNickname()))
                .collect(Collectors.toList());
        return new Result(collect);
    }

}
  • 엔티티를 DTO로 변환해서 반환하여 엔티티가 변해도 API스폑이 변경되지 않도록 함.
  • 추가로 Result 클래스로 컬렉션을 감싸서 향후 필요한 필드를 추가할수 있도록 함.

 

 

회원조회 API 테스트

 

728x90
320x100