react-spring 연결-rest api

ttomy·2022년 3월 12일
2

깃허브링크: https://github.com/hum02/writequiz

react는 클라이언트 사이드 렌더링을 하는 라이브러리이다.

html-javascript 코드를 로드 ---> 콘텐츠를 화면에 보이기 둘 사이에
api로부터 데이터를 fetch 해 오는 과정이 존재한다. 이 과정이 바로 백엔드와 프론트엔드의 연결점이 되는 지점이다. 아래에서 구체적인 과정을 살펴보자.

react: 프론트엔드에서 api로부터 데이터 받기

  • App.js
class App extends Component {
    render() {
        return (
          <React.Fragment>
              <Background>
                 <Routes>
                    <Route path="/" element={<Home/>} />
                    <Route path="/allpost" element={<About/>}/>
                    <Route path="/write/:id" element={<Write/>}/>
                    <Route path="/write" element={<Write/>}/>
                 </Routes>
              </Background>
          </React.Fragment>
        );
    }
}

/allpost라는 url에 < About/>이 그려지게 라우팅을 한 것을 볼 수 있다. About.js에서는 포스트글들에 대한 내용을 Rest api를 통해 가져온 후 브라우저에 보여준다.
아래 About.js의 axios.get문을 통해 데이터를 가져오는 것을 확인할 수 있다.
언급했듯 JS코드의 Rest api(8080/api/articles)에서 데이터를 가져오는 axios나 fetch문이 백엔드에서 데이터를 가져오는 연결점이 되었다.

  • About.js
function About() {

  const [articles, setArticles] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const fetchArticles = async () => {
    try {

      setArticles(null);

      // api로부터 article정보 가져오기
      const response = await axios.get(
        'http://localhost:8080/api/articles'
      );
      
      setArticles(response.data); 
    } catch (e) {
      setError(e);
    }
    setLoading(false);
  };

  useEffect(() => {
    fetchArticles();
  }, []);

  if (loading) return <div>로딩중..</div>;
  if (error) return <div>에러가 발생했습니다</div>;

  if (!articles) return null;

  return (
		<div>
	    <ul>
	      {articles.map(articles => (
	        <li key={articles.id}>
	          title: {articles.title}
	          <div></div>
	          content: {articles.contents}
	        </li>
	      ))}
	    </ul>
		    --button을 클릭하면 API를 다시 불러와줍니다.
			<button onClick={ fetchArticles }>다시 불러오기</button>
		</div>
  );

}

export default About;

/write 페이지에서 출간한 포스트를 /allpost에서 확인한다.

const createPost = (post) => {
  return function (dispatch, getState, { history }) {
    console.log('createPost', post);
    axios.post(`/api/articles`, post).then((res) => {
      history.push('/');
    });
  };
};
  • /allpost
    About.js가 그려진 모습

spring: 백엔드에서 rest api 통해 데이터 넘겨주기

프론트에서 백엔드의 데이터를 잘 받아 화면에 그리려면 백엔드 입장에서는 프론트(Javascript코드)가 접근할 api url에 적당한 데이터를 넣어야한다. 아래가 그 예이다.

  • 8080/api/articles에 json 데이터를 반환

spring에서는 @RestController 를 통해 rest api서비스를 구현할 수 있다.

본 프로젝트에서는 ArticleRestController 에서 포스트들을 저장하고, 조회하고, 수정하고, 삭제하는 rest api를 구현한다.

RequiredArgsConstructor
@RestController
public class ArticleRestController {

    private final ArticleService articleService;

	//게시글 조회
    @GetMapping("/api/articles")
    public List<Article> getAllArticle(){
        return articleService.getArticles();
    }

    // 게시글 작성
    @PostMapping("/api/articles")
    public Article createArticle(@RequestBody ArticleRequestDto articleRequestDto){
        System.out.println("Post-Article!!!");
        return articleService.createArticle(articleRequestDto);
    }
    //게시글 삭제
    @DeleteMapping("/api/articles/{id}")
    public Long deleteArticle(@PathVariable Long id){
        articleService.deleteArticle(id);
        return id;
    }

    //게시글 수정
    @PutMapping("/api/articles/{id}")
    public Long updateArticle(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto){
        return articleService.update(id, articleRequestDto);
    }
}

Rest api의 좋은 구현?

rest Api를 설계할 때는 URL로 자원을 명시하고,HTTP method로 행위를 명시해야한다.
이게 무슨말인가 하면 rest api의 url에 가능하면 동사(행위)를 사용하지 않는다는 것이다.
사용자의 정보를 보여주는 rest api에 /viewuser 과 같은 url이름을 주기보다 /user 이라는 이름을 주고 get메소드를 통해 조회를 하는 것이 바람직하다.
같은 url이더라도 메소드가 get이냐 put이냐에 구분되어 다른 기능을 수행한다. 아래는 공식문서의 예에서도 이 원칙이 지켜진다.


@RestController
class EmployeeController {

  private final EmployeeRepository repository;

  EmployeeController(EmployeeRepository repository) {
    this.repository = repository;
  }


  // Aggregate root
  // tag::get-aggregate-root[]
  @GetMapping("/employees")
  List<Employee> all() {
    return repository.findAll();
  }
  // end::get-aggregate-root[]

  @PostMapping("/employees")
  Employee newEmployee(@RequestBody Employee newEmployee) {
    return repository.save(newEmployee);
  }

  // Single item
  
  @GetMapping("/employees/{id}")
  Employee one(@PathVariable Long id) {
    
    return repository.findById(id)
      .orElseThrow(() -> new EmployeeNotFoundException(id));
  }

  @PutMapping("/employees/{id}")
  Employee replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) {
    
    return repository.findById(id)
      .map(employee -> {
        employee.setName(newEmployee.getName());
        employee.setRole(newEmployee.getRole());
        return repository.save(employee);
      })
      .orElseGet(() -> {
        newEmployee.setId(id);
        return repository.save(newEmployee);
      });
  }

  @DeleteMapping("/employees/{id}")
  void deleteEmployee(@PathVariable Long id) {
    repository.deleteById(id);
  }
}

url이 employees로 일정하지만 get,post,put,delete 에 따라 메소드에 해당하는 기능을 수행한다.
ArticleRestController에서도 마찬가지로 mapping되는 url은 /api/articles로 일정하고
getmapping/ postmapping/ putmapping/ deletemapping 이냐에 따라
조회, 저장, 수정 ,삭제 기능이 메소드에 맞게 수행된다.

url을 리소스로하며 Json을 반환하고, http 메소드에 맞게 동작을 수행하고, url이름으로 접근하는 자원이 설명되는 정도의 api를 rest api라며 사용하기도 하지만, 이는 사실 잘못된 표현이다 rest api는 그 이상의 제약조건들이 있다.
이를 모두 충족시키는 것이 진정 바람직한 rest api라는 점에서 rest api란 무엇인지 깊게 생각해볼 필요가 있다.

위의 글과 영상에서 rest api에 대한 더 자세한 내용을 알 수 있다.

+참고:
https://github.com/yooooonk/hh99_clone
https://spring.io/guides/tutorials/rest/

0개의 댓글