본문 바로가기

Java/Springboot

[나만의 블로그] spring 회원가입

728x90

[나만의 블로그] spring 회원가입

 

앞에 spring을 붙인 이유는 백만 구현했기 때문.

 

 

🌵 DTO

Data Transfer Object
주로 View와 Controller 사이에서 데이터를 주고 받을 때 활용한다.
DTO는 getter/setter 메소드를 포함. 그래서 난 getEmail(), getPassword() 메서드를 정의해 준 적이 없는데, 테스트 코드를 작성할때 사용할 수 있었다.

📜 service/UserService.java

userDTO.getEmail();


setter가 아닌 생성자를 이용해 초기화하는 경우 객체 값이 변경되지 않는다.

메소드 호출 수를 줄이기 위해 프로세스 간에 데이터를 전달하는 개체
데이터를 캡슐화하여 애플리케이션의 한 부분에서 다른 부분으로 보내는 데 자주 사용
클라이언트와 서버 간 또는 애플리케이션의 서로 다른 계층 간에 데이터를 전송해야 하는 경우 또는 분산 시스템에서 사용


🌵 VO

Value Object
값 자체를 표현하는 객체이다.
객체들의 주소가 달라도 값이 같으면 동일하다 본다.
getter메소드는 있지만, setter 메소드는 가지지 않는다.

도메인의 설명적인 측면을 나타내는 객체

 

🌵 Entity

실제 DB 데이블과 매핑되는 핵심 클래스
이를 기준으로 SQL문을 사용하지 않아도 테이블이 생성되고 스키마가 변경된다.절대로 Entity를 요청이나 응답값을 전달하는 클래스로 사용해서는 안된다. 왜그럴까? 데이터 무결성 개념을 떠올려 보자!
만약, Entity값을 요청하고 전달해 우리가 원치 않는 데이터 형태가 DB에 저장되면 어떻게 될까?

Entity가 id로 구분되는 점도 데이터 무결성과 관련있다.

📜 repo/UserRepository

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
}

JpaRepository<UserEntity, id타입>

id값은 고유값을 의미한다. 보통 자동으로 값이 커지는 정수(auto Increment)를 사용한다.

 

❓ Entity 와 DTO를 굳이 구분한 이유가 보이는가?

데이터의 흐름과 제어를 통해 데이터 베이스의 무결성을 간접적으로 보장해준다.

 

 

📜 dto/UserDTO.java

package com.example.demo.dto;

import com.example.demo.entity.UserEntity;

import lombok.*;

@AllArgsConstructor
@Data
public class UserDTO {
    private Long id;
    private String email;
    private String password;
    private String name;

    public UserEntity toEntity() {
        return UserEntity.builder()
        .userEmail(email)
        .userPw(password)
        .userName(name)
        .build();
    }
}

 

📜 entity/UserEntity.java

package com.example.demo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.*;

@NoArgsConstructor
@Data
@Entity(name="USER")
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String userEmail;

    @Column
    private String userPw;

    @Column
    private String userName;

    @Builder
    public UserEntity(
        String userEmail,
        String userPw,
        String userName) {
            this.userEmail = userEmail;
            this.userPw = userPw;
            this.userName = userName;
        }
}

 


🌵 JSON 직렬화/ 역직렬화

📜 controller/userController.java

@RestController
@AllArgsConstructor
public class UserController {
    
    private final UserService userService;	

    @PostMapping("/user/save")
    public String saveUser(@RequestBody UserDTO userDTO) {
        System.out.println(userDTO);
        userService.saveUser(userDTO); 
        return "0";
    }
}

바디로 회원가입 정보를 받고 db에 저장하려고 하는데 발생한 InvalidDefinitionException 오류!

 

검색결과, 직렬화 또는 역직렬화되는 클래스에는 기본(인수 없음) 생성자가 있어야 합니다. 클래스에 개체가 없으면 Json이 개체를 제대로 인스턴스화하지 못해 InvalidDefinitionException이 발생할 수 있다.

 

📜 dto/UserDTO.java

실제로 기본생성자를 추가하고 나니 데이터 베이스에 잘 저장되었다.

 

 

❓ 그렇다면, JSON의 직렬화, 역직렬화는 뭘까?

Java 객체와 해당 JSON 표현 간의 데이터 변환을 포함하는 프로세스를 의미한다.

직렬화 : Java 객체를 JSON 표현으로 변환하는 프로세스
역직렬화 : JSON 표현을 다시 Java 객체로 변환하는 프로세스

ObjectMapper는 리플렉션을 사용해 역직렬화를 하려면 기본생성자를 선언해주어야한다.
리플렉션은 JVM이 클래스 로더를 통해 읽어온 클래스 정보를 JVM 메모리에 저장하는 것을 의미한다.


 🌵 회원가입 관련 테스트 코드

📜 테스트 폴더/user/MemberTest.java

package com.example.demo.user;

// 테스트 용으로 클래스를 사용하겠다는 어노테이션
@SpringBootTest
public class MemberTest {

    @Autowired // 테스트 공간이기 때문에 생성자 주입 대신 클래스 주입을 해도 상관없음.
    private UserService us;

    @Test // 테스트 메서드 선언
    @DisplayName("회원 데이터 생성")
    public void newMembers() {
        IntStream.rangeClosed(1, 15).forEach(i -> {
            us.saveUser(new UserDTO("email" + i, "pw" + i, "name" + i));
        });
    }

    @Test
    @DisplayName("회원 테이터 조회")
    public void checkMembers() {
        for(int i=1; i<16; i++) {
            UserEntity userEntity = us.getUserById("email" + i);
            assertEquals("pw"+i, userEntity.getUserPw());
            assertEquals("name"+i, userEntity.getUserName());
        }
    }
}

 

📜 controller/userController.java

@RestController
@AllArgsConstructor
public class UserController {
    
    private final UserService userService;	

	// http://localhost:8080/user/info/email6
    @GetMapping("/user/info/{id}")
    public UserEntity getUserById(@PathVariable String id) {
        return userService.getUserById(id);
    }

	// http://localhost:8080/user/info/email6?name=영희
    @PutMapping("/user/info/{id}")
    public void modifiedUserById(@PathParam("name") String name, @PathVariable String id) {
        userService.modifiedUserById(name, id);
    }
}

 

📜 service/userService.java

@Service
@AllArgsConstructor
public class UserService {
    
    private UserRepository userRepository;

    public UserEntity getUserById(String id) {
        return userRepository.getUserById(id);
    }

    public void modifiedUserById(String name, String id) {
        userRepository.modifiedUserById(name, id);
    }

}

 

 

📜 repo/userRepository.java

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
    @Query(value = "select * from user where user_email = :id", nativeQuery = true)
    UserEntity getUserById(@Param("id") String id);

    @Modifying
    @Query(value = "update user set user_name = :name where user_email = :id", nativeQuery = true)
    void modifiedUserById(@Param("name") String name, @Param("id") String id);
}

jpa 에 익숙치 않아 일단 익숙한 sql문으로 회원가입이 잘 되었는지 회원정보 조회, 수정을 해봤다.

get mothod 수행까지는 수월하게 테스트 통과했으나, put method에서 오류 발생. server internal error 발생!

 

검색해 트랜잭션 수행 연산을 추가하니 수정되었다.

 

🌵 트랜잭션이란?

데이터 베이스의 상태를 바꾸기 위해 수행되는 작업의 단위

 

❓ 데이터베이스의 락이 있다.

트랜잭션이 처리되는 순서를 보장해 데이터의 무결성을 유지하기 위해 공유락과 베타락이 있다.

공유락 : 데이터를 읽기 위한 락, 데이터가 변경되지 않는다.

베타락 : 데이터를 수정하는 락, 하나의 트랜잭션을 수행 중일 경우 다른 트랜잭션이 접근할 수 없다.

 

put method를 활용해 데이터 베이스 값을 변경하려고 했으니, 트랜잭션을 사용하지 않으면 오류가 발생하는 것이었다!

데이터 무결성에 진심인 spring이다.

 


 

🌵 회원가입 기능 develop방향

비밀번호 암호화 저장

회원email 과 일치하면 회원가입이 불가능한 예외처리


🪴 참고 자료

https://tecoble.techcourse.co.kr/post/2021-05-16-dto-vs-vo-vs-entity/

 

DTO vs VO vs Entity

DTO와 VO는 분명히 다른 개념이다. 그런데, 같은 개념으로 생각해서 사용하는 경우가 많다. 왜일까? ⌜Core J2EE Patterns: Best Practices and Design Strategies⌟ 책의 초판에서는 데이터 전송용 객체를 로 정의

tecoble.techcourse.co.kr

https://velog.io/@hgo641/Spring%EC%97%90%EC%84%9C%EC%9D%98-%EC%A7%81%EB%A0%AC%ED%99%94JSON-parse-error-%ED%95%84%EB%93%9C%EA%B0%80-%ED%95%98%EB%82%98%EB%B0%96%EC%97%90-%EC%97%86%EB%8A%94-DTO

 

Spring에서의 직렬화

Spring은 ObjectMapper라는 클래스를 사용해 Json값을 Spring의 자바 객체로 변환한다. spring-boot-starter-web을 gradle에 의존성으로 추가하면 jackson이라는 라이브러리도 함께 가져오는데, jackson안에 ObjectMa

velog.io

 

'Java > Springboot' 카테고리의 다른 글

[나만의 블로그] DB 연결  (0) 2023.11.24
[나만의 블로그] 프로젝트 계획  (1) 2023.11.24