오랜만에 Jelog 프로젝트를 다시 진행했다.
그동안 배웠던 스프링 부트를 활용해서, 계정 정보와 게시글 정보를 서버와 연결하여 연동하고자 한다.
우선, 스프링 부트와 연동하기위해서는 기존에 Jelog 에서 사용하던 로컬 히스토리와 로그인, 계정 생성, 게시글 포스팅 모든 기능을 수정해야한다.
그렇기에, 스프링 부트에서 우리가 호출할 API 들을 명세하는 것을 가장 첫번째 단계로 진행했다.
가장 먼저 회원 가입 기능을 하는 singup
API
POST
, /api/register
, { 'email' : String, 'password' : String, 'userID' : String, 'icon' : String}
으로 명세한다.
로그인 기능을 하는 login
API
POST
, /api/login
, { 'email' : String, 'password' : String }
으로 명세한다.
계정 정보를 받아오는 getAccount
API
GET
, /api/login/{email}
, email
은 @PathVariable
이다.
강의를 들으면서 기능의 흐름의 역순으로 코드를 작성하는게 더 작성하기 편하다고 들었다.
그래서, DB 에서 부터 서비스, 컨트롤러 순으로 코드를 작성했다.
// Account.java
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "accounts")
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "email", nullable = false, unique = true)
private String email;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "userID", nullable = false)
private String userID;
@Column(name = "icon")
private String icon;
@Builder
public Account(String email, String password, String userID, String icon){
this.email = email;
this.password = password;
this.userID = userID;
this.icon = icon;
}
public String toJson() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
}
DB
에 저장될 Entity
가 될 Account
클래스이다.
PK
는 id
로 설정하고, 고유 번호가 자동 생성 되도록 어노테이션을 붙여준다.
// AccountRepository.java
public interface AccountRepository extends JpaRepository<Account, Long> {
Optional<Account> findByEmail(String email);
}
Account
저장소로 사용할 repository
를 interface
형식으로 생성하고,JpaRepository
를 상속받아 Spring Data JPA 를 사용한다.
기본적으로 Spring Data JPA 가 제공하는 메소드 이외로 사용할 메소드는 내가 직접 작성한다.
Optional<Account> findByEmail(String email)
메소드는 이메일을 입력으로 받고, 저장소에 해당 이메일에 해당하는 Account 가 있는지 없는지를 반환한다.
// AddAccountRequestDto.java
@Getter
@Setter
public class AddAccountRequestDto {
private String email;
private String password;
private String userID;
private String icon;
}
계정 생성시 들어올 요청에 대한 Dto
이다.
// AccountService.java
@RequiredArgsConstructor
@Service
public class AccountService {
private final AccountRepository accountRepository;
public Long save(AddAccountRequestDto requestDto){
return accountRepository.save(
Account.builder()
.email(requestDto.getEmail())
.password(requestDto.getPassword())
.userID(requestDto.getUserID())
.icon(requestDto.getIcon())
.build()
).getId();
}
public Boolean login(LoginRequestDto requestDto){
Optional<Account> result = accountRepository.findByEmail(requestDto.getEmail());
return result.get().getPassword().equals(requestDto.getPassword());
}
public Account getAccount(String email){
return accountRepository.findByEmail(email).get();
}
}
AccountRepository
와 동작할 서비스인 AccountService
를 생성한다.
필요한 어노테이션들을 달아주어 Bean
에 정상적으로 등록될 수 있도록 한다.
save
메소드를 통해서, requestDto
로 입력받은 이메일, 패스워드, 아이디, 아이콘 정보를 Account
의 빌더를 통해서 새로운 Account
객체를 생성하고 accountRepository
의 save
메소드로 넘겨준다.
login
매소드는 requestDto
로 입력받은 이메일과 패스워드가 서로 매칭되는지 확인한다.
getAccount
메소드는 입력받은 이메일에 해당하는 계정 정보를 반환한다.
// AccountApiController.java
@RequiredArgsConstructor
@Controller
@RequestMapping("/api")
public class AccountApiController {
private final AccountService accountService;
@PostMapping("/register")
public ResponseEntity<Map<String, Object>> signup(@RequestBody AddAccountRequestDto requestDto){
accountService.save(requestDto);
Map<String, Object> response = new HashMap<>();
response.put("Message", "Account created Successfully");
return ResponseEntity.ok(response);
}
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequestDto requestDto){
System.out.println("requestDto = " + requestDto.getEmail());
System.out.println("requestDto = " + requestDto.getPassword());
Boolean result = accountService.login(requestDto);
System.out.println("result = " + result);
if(result) return ResponseEntity.ok("Login Success");
else return ResponseEntity.badRequest().build();
}
@GetMapping("/login/{email}")
public ResponseEntity<String> getAccount(@PathVariable String email){
return ResponseEntity.ok(accountService.getAccount(email).toJson());
}
}
프론트엔드 단에서 호출할 API
를 설정하는 AccountApiController
클래스이다.
회원 가입 API
의 호출 메서드는 POST
로 지정하고, URL 은 /api/register
로 설정한다.
파라미터로 받을 AddAccountRequestDto
에는 이메일, 비밀번호, 유저명, 아이콘 정보가 포함되어 있다.
ResponseEntity
를 결과로 반환한다.
로그인 API
의 호출 메서드는 POST
로 지정하고, URL 은 /api/login
로 설정한다.
로그인에 필요한 정보를 파라미터로 입력받는다. 해당 파라미터에는 이메일과 비밀번호 정보가 포함되어 있다.
이메일 값을 @PathVariable
로 받는다.
포스트맨 프로그램을 통해 테스트를 했을 때, 모든 API
가 정상적으로 동작하는 모습이다.
// header.js
const onClickLoginButton = () => {
if (modalState === "login") {
axios
.post("/api/login", {
email: userID,
password: userPW,
})
.then((res) => {
axios
.get(`/api/login/${userID}`)
.then((res) => {
console.log(res.data);
const user = res.data;
setUSER(user);
setAccount(user);
setIsLogin(true);
userIconButtonRef.current.style.backgroundColor = "";
setShowModal(false);
navigate("/");
})
.catch((err) => console.log(err));
})
.catch((err) => {
console.log(err);
showAlertPopUp();
});
} else {
if (isEmailVaild()) {
const userIcon =
"#" + Math.floor(Math.random() * 0xffffff).toString(16);
axios
.post("/api/register", {
userID: userID.split("@")[0],
password: userPW,
email: userID,
icon: userIcon,
})
.then((res) => {
console.log(res);
생략...
})
.catch((err) => {
console.log(err);
showAlertPopUp();
});
}
}
};
header.js 의 로그인, 회원가입을 담당하는 기능인 onClickLoginButton
메소드를 수정했다.
만약, 현재 창이 로그인 화면을 보여주고 있는 경우, 입력받은 이메일과 패스워드를 axios
를 이용하여 /api/login/
으로 POST
요청을 보낸다.
올바른 이메일과 비밀번호를 입력하여 정상적인 응답이 돌아오면 USER
를 설정해주기 위한 /api/login/{email/
을 호출하여 계정 정보를 받아온다.
현재 창이 회원가입 화면을 보여주고 있는 경우, 입력받은 값들을 /api/register
를 호출하면서 requestBody
값으로 넘겨준다.