Back-end/나만의 API

API (1) - Login API

HOONY_612 2021. 7. 7. 19:42
반응형

스프링을 조금 배워서 실제로 사용해볼려고 한다.

API를 하나씩 만들어가며 나만의 포트폴리오를 완성할려고 한다. 첫 번째 API는 간단한 Login API를 만드는 것이다.

 

- IDE : Itellij

- Builder : Gradle

- Server : Spring-boot

- DB : H2

 

구현 목표는 아래의 그림과 같다.

 

 

- User

이것은 정보를 가진 주요 객체이다. id와 name 두 개의 필드를 가지고 DB로 접근한다.

 

- UserController

이 부분은 url 맵핑과 get, post를 이용하여 json 및 html파일 이름을 주고 받는다.

 

- UserService

핵심로직이 담긴 Bean이다. 인터페이스를 정해놔 언제든지 변경 가능하도록 만들어 놨다.

 

- UserRepository

DB에 어떤 방식으로 접근 해야하는지 결정하는 부분이다. 나는 JdbcTemplete과 Memory에 직접 저장한 DB를 가져오는 방식을 구현하였다.

 

- H2

H2는 간단한 관계형 데이터 베이스 형태로 가볍게 사용 할 수 있다. JDBC API도 지원해주기 때문에 선택했다.

 

 

내가 프로젝트를 작성한 순서에 맞게 설명하겠다.

 

1. DB구축 

DB를 구축해야하는데 H2를 다운받고 H2를 실행시키면 다음과 같은 창이 뜬다.

 

위와 같은 창이뜨면 JDBC URL을 복사하여 프로젝트 파일 중 application.properties파일에 다음과 같이 작성한다.

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

 

그러면 아래 화면이 나오고 테이블이 있으면 DROP TABLE [TABLE명]을 실행해주자.

일단 우리는 User라는 객체는 id와 name 두 가지 필드를 가지고 있다. 테이블을 생성해보자.

 

이렇게 간단한 DB구축이 끝났다.

 

2. Repository 인터페이스 구축 및 객체 구현

Repository구현 방식은 프로그램 자체에 DB를 구축하는 Memory방식, SQL로 DB에 접근하여 DB를 활용하는 JdbcTemplete방식, SQL이 필요없이 간단하게 접근 할 수 있는 JPA방식, JPA방식을 spring에서 더욱 쉽게 사용할 수 있는 Spring JPA방식 크게 4가지가 있다. 나는 SQL문에 익숙하지 않기에 이번에 SQL을 사용하기로 해서 JdbcTemplete방식과 Memory방식을 선택했다. 

 

4가지 메서드를 구현했다. DB에 ID,NAME을 넣는 과정, DB를 ID,NAME으로 각각 가져오는 과정, 전체 리스트를 가져오는 과정이다. 그럼 JdbcTemplete으로 데이터를 가져오는 과정을 확인해보자.

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public JdbcTempleteUserRepository(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

이 JdbcTemplete객체는 자동으로 DB와 connection하고 sql문을 보내주고 처리하는 역할을 하는 클래스이다.

@Autowirde는 나중에 Repository가 바뀔 수 있어서 Config파일로 따로 관리하고 있어서 Config파일로부터 주입받았다.

여기서 간단하게 query method에 대해서 쓸 것이다.

 

query는 다양하게 오버라이드 되어 쓰이고 있다.

우리가 사용 할 query는 다음과 같다.

query(String sql, org.springframework.jdbc.core.RowMapper<T> rowMapper, @Nullable Object... args)

중간에 RowMapper는 DB에서 가져온 데이터들을 JAVA객체로 변환시켜주는 역할을 한다.

    private RowMapper<User> userRowMapper() {
        return (rs, rowNum) -> {
            User user = new User();
            user.setId(rs.getLong("id"));
            user.setName(rs.getString("name"));
            return user;
        };

 

그리고 전체적인 Method의 구현이다. save같은 경우는 RowMapper가 필요없다. 왜냐면 JAVA객체로 변환이 필요없기 때문이다. "?"부분에 우리가 넣은 값들이 들어간다.

    @Override
    public User save(User user) {
        jdbcTemplate.update("insert into users (id,name) values (?,?)"
                ,user.getId()
        ,user.getName());
        return null;
    }

    @Override
    public Optional<User> findById(Long id) {
        List<User> result = jdbcTemplate.query("select * from users where id = ?"
        ,userRowMapper()
        ,id);
        return result.stream().findAny();
    }

    @Override
    public Optional<User> findByName(String name) {
        List<User> result = jdbcTemplate.query("select * from users where name = ?"
        ,userRowMapper()
        ,name);
        System.out.println("result.stream().findAny() = " + result.stream().findAny());
        return result.stream().findAny();
    }

    @Override
    public List<User> findAll() {
        return jdbcTemplate.query("select * from users", userRowMapper());
    }

 

 

3. Service구현

Service구현은 핵심 비지니스로직을 넣는 곳이다. 즉 DB에서 나오고 들어가는 데이터들을 가공하는 곳이라고 생각하면 된다. Repository부분과 비슷해 보이지만 역할이 완전 다르다. Repository의 주 역할은 DB접근 방법 결정, Service가 우리가 진짜 구성해야하는 로직을 적어야한다. 두 개를 합칠 수 있지만 Java는 최대한 모듈화해서 역할을 나눠야하기 때문에 나누게 되었다.

public class UserServiceImpl implements UserService{

    private final UserRepository userRepository;

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public Long join(User user) {
        duplicateUser(user);
        userRepository.save(user);
        return user.getId();
    }

    @Override
    public List<User> findUsers() {
        userRepository.findAll();
        return userRepository.findAll();
    }

    @Override
    public Optional<User> findOne(Long userId) {
        return userRepository.findById(userId);
    }

    private void duplicateUser(User user) {
        userRepository.findByName(user.getName())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
    }
}

위와 같이 중복 된 이름을 못 넣게 해놨다. 넣으면 이미 존재하는 회원입니다라는 문구가 나온다.

 

4. Controller로 Url맵핑

@Controller
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/")
    public String HomeController() {
        return "Home";
    }

    @GetMapping(value = "/new")
    public String createUser() {
        return "createUser";
    }

    @PostMapping(value = "/new")
    public String createUser_P(UserForm userForm) {
        User user = new User();
        user.setName(userForm.getName());
        userService.join(user);
        return "redirect:/";
    }

    @GetMapping(value = "/users")
    @ResponseBody
    public List<User> receiveUser() {
        List<User> res = userService.findUsers();
        return res;
    }
}

 

위의 코드는 Controller부분의 구현 코드이다.

원래 Mapping되어진 메소드의 리턴 값은 "templete" + 리턴 값 + ".html"ViewResolver가 실행시킨다.

그러나 @ResponseBody를 붙이면 리턴 값은 HTTP통신 중 바디 부분에 들어가 JSON포맷으로 클라이언트에게 전달된다. 

 

- "/" : /templete/Home.html 파일 실행

- "/new" :  /templete/createUser.html 파일 실행 및 Form내용 보내고 "/"으로 되돌아가기

- "/users" : 객체 List<User> JSON형태로 클라이언트에게 전달.

 

 

5. 점검

<localhost:8080/>

>

<localhost:8080/new>

<localhost:8080/users>

위의 JSON형태의 데이터를 가지고 프론트엔드 단에서 꾸며서 보여준다.

 

이렇게 하나의 Login하는 과정을 간략하게 만들어봤다. 다 만드니 뿌듯함이 밀려온다.

다음에는 주문 관리해주는 API를 만들어보고 싶다. 쇼핑몰에서 쓰는 방식이니 대중화되있어 분명 도움이 될 것이다.

반응형