빅데이터 Java 개발자 교육 [Spring - (Controller, RestAPI방식의 차이)]

Jun_Gyu·2023년 5월 15일
0
post-thumbnail

Controller 방식과 RestApi(controller)방식의 차이

기존의 Controller 방식과 Rest방식 두가지를 이용하여 데이터를 삽입해보도록 하겠다.

먼저 Controller 방식을 이용해 도서정보를 insert 해보자.

insert

Library2Controller

@PostMapping(value="/insert.do")
public String postMethodName(@ModelAttribute Library2 obj) {
        try {
            log.info("format", obj.toString());
            l2Repository.save(obj);
            return "redirect:/library2/insert.do";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/home.do";
        }        
        
    }
    
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    
    <h2>도서등록</h2>
    <hr />
    <form th:action="@{/library2/insert.do}" method="post">
        <input type="text" name="name" />
        <input type="text" name="address" />
        <input type="text" name="phone" />
        <input type="submit" value="등록">
    </form>

</body>
</html>


DB에 성공적으로 데이터가 들어갔음을 확인할 수 있다.


RestLibrary2Controller

@Slf4j
@RestController
@RequestMapping(value = "/api/library2")
@RequiredArgsConstructor

public class RestLibrary2Controller {
    final String format="RestLibrary2 => {}";
    final Library2Repository l2Repository;

    // 127.0.0.1:9090/ROOT/api/library2/insert.json
    // @RequestBody Library2 obj    => 기본으로 보낼 때
    // @ModelAttribute Library2 obj => 위의 방식으로 불가능할때 이미지
    @PostMapping(value="/insert.json")
    public Map<String, Object> insertPOST(@RequestBody Library2 obj) { // insert 반환타입은 Map으로 해야함.
        Map<String, Object> retMap = new HashMap<>();
        try {
            log.info(format, obj.toString());
            l2Repository.save(obj);
            retMap.put("status", 200);
        } catch (Exception e) {
            e.printStackTrace(); // 개발자용 debug
            retMap.put("status", -1);
            retMap.put("error", e.getMessage());
        }
        return retMap;
    }
    
}

postman을 통해서 데이터를 삽입해보면

status:200이 표시되면 성공적으로 코드가 작동했으며, DB에 데이터가 저장되었다는 의미이다.


rest의 방식인 경우에는 프론트에서 제작한 html(화면)이 존재하지 않을때 DB로 자료를 전송하기 위함이다.

이번에는 등록된 list들을 불러와 보도록 하겠다.
먼저 Controller 방식이다.

SelectList

Library2Controller

@GetMapping(value = "/selectlist.do")
    public String selectlistGET(Model model) {
        try {
            List<Library2> list = l2Repository.findAllByOrderByNameAsc();

            // [클래스, 클래스]
            log.info(format, list.toString());
            model.addAttribute("list", list);
            return "/library2/selectlist";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/home.do";
        }
    }

l2RepositoryLibrary2Repository의 객체이다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h2>도서목록 조회</h2>
    <hr />
    <table border="1" style="display: contents;">
        <thead style="text-align: center;">
            <tr style="color: rgb(221, 65, 72); background-color: rgb(252, 221, 221);">
                <td>코드</td>
                <td>도서명</td>
                <td>주소</td> 
                <td>전화번호</td>
                <td>등록일자</td>
            </tr>
        </thead>
        <tbody>
            <tr th:each="obj : ${list}">
                <td th:text="${obj.code}"></td>
                <td th:text="${obj.name}"></td>
                <td th:text="${obj.address}"></td>
                <td th:text="${obj.phone}"></td>
                <td th:text="${obj.regdate}"></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

주소를 Chrome에 입력하면 위와같이 방금 등록한 도서들을 확인할 수 있다.


이번에는 RestAPI 방식이다.

RestLibrary2Controller

// 127.0.0.1:9090/ROOT/api/library2/selectlist.json
    @GetMapping(value="/selectlist.json")
    public Map<String, Object> selectListGET() {
        Map<String, Object> retMap = new HashMap<>();
        try {
            List<Library2> list = l2Repository.findAllByOrderByNameAsc();
            log.info(format, list.toString());
            retMap.put("status", 200);
            retMap.put("list", list);
        } catch (Exception e) {
            e.printStackTrace(); // 개발자용 debug
            retMap.put("status", -1);
            retMap.put("error", e.getMessage()); // 실패시 -1과 오류 전송
        }
        return retMap;
    }

위와 같이 코드를 구성한 이후, postman에서 아래와 같이 주소를 잡아준다.

GET을 이용한 조회의 경우에는 별다른 데이터 삽입이 필요없다.

성공적으로 List를 불러온 모습이다.


다음은 학생정보를 등록해보도록 하겠다.

insert

Student2Controller

package com.example.controller.jpa;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import com.example.entity.library.Student2;
import com.example.repository.Student2Repository;




@Controller
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/student2")
public class Student2Controller {
    
    final String format = "s2Controller => {}";
    final Student2Repository s2Repository;
    BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();

    // 
    @GetMapping(value="/insert.do")
    public String insertGET() {
        try {
            return "/student2/insert";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/home.do";
        }
    }
    

    // http://127.0.0.1:9090/ROOT/student2/insert.do
    @PostMapping(value="/insert.do")
    public String insertPOST(@ModelAttribute Student2 obj) {
        try {
            // 암호는 bcpe를 이용하여 암호화하기
            obj.setPassword(bcpe.encode( obj.getPassword()));
            log.info(format, obj.toString());
            s2Repository.save(obj);
            return "redirect:/student2/insert.do";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/home.do";
        }
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h2>학생정보 등록</h2>
    <hr />
    <form th:action="@{/student2/insert.do}" method="post">
        <input type="text" name="email" placeholder="이메일"/>
        <input type="text" name="password" placeholder="비밀번호"/>
        <input type="text" name="name" placeholder="이름"/>
        <input type="text" name="phone" placeholder="전화번호"/>
        <input type="submit" value="등록">
    </form>
    
</body>
</html>

Chrome에서 화면을 실행시키면 아래와 같이 표시되게 된다.

등록을 누르게 되면 DB에도 적용이 되게 된다.

여기서 Password에 복잡한 해쉬값이 적용된것을 확인할 수 있는데, 이는 Controller에서
BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();
와 같이 비밀번호를 암호화해주는 Encoder를 사용하였기 때문이다.


이번에는 RestAPI 방식이다.

RestStudent2Controller

package com.example.restcontroller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.entity.library.Student2;
import com.example.repository.Student2Repository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping(value = "/api/student2")
@RequiredArgsConstructor
public class RestStudent2Controller {
    
    final String format="RestStudent2 => {}";
    final Student2Repository s2Repository;

    
    @PostMapping(value="/insert.json")
    public Map<String, Object> insertPOST(@RequestBody Student2 obj) { // insert 반환타입은 Map으로 해야함.
        Map<String, Object> retMap = new HashMap<>();
        try {
            log.info(format, obj.toString());
            s2Repository.save(obj);
            retMap.put("status", 200);
        } catch (Exception e) {
            e.printStackTrace(); // 개발자용 debug
            retMap.put("status", -1);
            retMap.put("error", e.getMessage());
        }
        return retMap;
    }
}

postman 워크스페이스에 위와같이 student2의 기능을 실행할 공간을 마련해준 뒤,

주소를 json으로 맞춰 POST 형식으로 들어갈 데이터들을 알맞게 집어넣었다.

status가 200을 나타내면 데이터가 올바르게 저장되었다는 뜻이다.

확인결과 두 데이터 모드 DB에저장되었다.

하지만 여기서 간과한것이 있는데, 암호화를 거치지 않았다.

RestController에 암호화 기능을 추가해서 다시 데이터를 넣어보도록 하자.

암호화 기능 추가

BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();


@PostMapping(value="/insert.json")
    public Map<String, Object> insertPOST(@RequestBody Student2 obj) { // insert 반환타입은 Map으로 해야함.
        Map<String, Object> retMap = new HashMap<>();
        try {
        	// 비밀번호 암호화
            obj.setPassword(bcpe.encode( obj.getPassword()));
            
            log.info(format, obj.toString());
            s2Repository.save(obj);
            retMap.put("status", 200);
        } catch (Exception e) {
            e.printStackTrace(); // 개발자용 debug
            retMap.put("status", -1);
            retMap.put("error", e.getMessage());
        }
        return retMap;
    }

다시 데이터를 넣어보도록 하자.

성공적으로 적용이 된 모습이다.

이번에는 추가적으로 레파지토리에 기능을 두가지 추가하여 RestAPI를 이용하여 기능수행을 시켜보도록 하겠다.

RestAPI 기능수행

기능은 총 두가지이다.

  • 가입된 이메일 중복확인
  • 이메일을 전달하면 회원의 이름과 연락처를 반환

Repository

먼저 Repository에 위의 두 기능을 수행해 줄 수 있도록 인터페이스를 구성해주었다.

package com.example.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.entity.library.Student2;
import com.example.entity.library.Student2Projection;

@Repository
public interface Student2Repository extends JpaRepository<Student2, String> {
    
    // 전달받은 이메일로 회원 이름과 연락처 조회
    // select name, phone from student2 where email = ?
    Student2Projection findByEmail(String email);

    // 이메일 중복확인
    // select count(*) from student2 where email = ?
    long countByEmail(String email);
}

전체정보를 조회하는것이라면 프로젝션이 필요없지만,

DB의 컬럼에서 이름과 전화번호만 필요로 하기 때문에 추가로 프로젝션을 만들어주어야 한다.

Projection 생성

package com.example.entity.library;

// 컬럼의 데이터 중에서 일부만 가져오기(Projection)
public interface Student2Projection {

    String getName();

    String getPhone();
}

위와같이 생성해준 뒤 RestAPIController를 수정해주도록 하자.

RestStudent2Controller

 // 이메일 중복확인용
    @GetMapping(value = "emailcheck.json")
    public Map<String, Object> emailcheckGET(@RequestBody Student2 obj) {
        Map<String, Object> retMap = new HashMap<>();
        try {
            long obj2 = s2Repository.countByEmail(obj.getEmail());
            retMap.put("status", 200);
            retMap.put("chk", obj2);
        } catch (Exception e) {
            e.printStackTrace(); //
            retMap.put("status", -1);
            retMap.put("error", e.getMessage());
        }
        return retMap;
    }



    // 이메일이 전달되면 회원의 이름, 연락처가 반환되는
    @GetMapping(value="/selectone.json")
    public Map<String, Object> selectoneGET(@RequestBody Student2 obj) {
        Map<String, Object> retMap = new HashMap<>();
        try {
            Student2Projection obj2 = s2Repository.findByEmail(obj.getEmail());
            retMap.put("status", 200);
            retMap.put("obj", obj2);
        } catch (Exception e) {
            e.printStackTrace(); // 개발자용 debug
            retMap.put("status", -1);
            retMap.put("error", e.getMessage());
        }
        return retMap;
    }

위와 같이 구성해준 뒤 postman에서 데이터 조회가 제대로 이루어지는지 확인해보도록 하겠다.

postman

먼저 기능을 수행할 공간을 마련해준 뒤, 이메일 중복확인부터 차례대로 확인해보겠다.

원래라면 대부분의 조회에서는 별도로 데이터를 입력할 필요가 없지만,
WHERE조건에서 데이터를 입력해주어야 하기 때문에 위와같이 확인하고자 하는 이메일 주소를 입력했다.

만일 이미 존재하는 이메일이 있다면 chk값이 1,

없다면 0으로 표시되게 된다.



이번엔 이메일에 해당하는 이름과 전화번호다.

조회하고자 하는 조건의 이메일을 입력하면

위와같이 해당하는 이메일의 정보가 표시되게 된다.

만일 존재하지 않는 이메일값을 입력하게 되면,

obj값은 null로 표출되게 된다.


Feedback

하지만 위와같이 @RequestBody를 통해서 데이터 출력을 구현하게되면 이후 프론트엔드 작업물과 결합하는 과정에서 제대로 실행이 되지 않는 상황이 발생할 수 있다. 이를 해결하기 위해서는 다음과 같이 코드를 재구성해야한다.

RestStudent2Controller,java

@GetMapping(value = "emailcheck.json")
public Map<String, Object> emailcheckGET(@RequestParam(name = "email") String email)
    
    
    
@GetMapping(value="/selectone.json")
public Map<String, Object> selectoneGET(@RequestParam(name = "email") String email)   

@RequestBody 어노테이션에서 @RequestParam으로 변경해준 모습이다.
이는 /ROOT/api/student2/emailcheck.json?email=이메일과 같이 추후 주소명을 세부적으로 지정하여 데이터별로 화면을 나눌때 쉽게 불러오고자 함이다.

postman에서도 아래와 같은 방식으로 데이터를 조회해야 한다.

postman

Params 항목에서 사용하고자 하는 Key 값과 Value값을 차례대로 입력해준 뒤, 결과를 조회하게 되면

마찬가지로 출력된 데이터를 확인할 수 있겠다.


이메일 중복확인에서도 똑같이 적용하면 된다!
profile
시작은 미약하지만, 그 끝은 창대하리라

0개의 댓글