프로젝트 구조
내부 코드 형태
App.js 코드
내부 폴더 구조 - post/user 생성
JoinForm / LoginForm 생성
Detail / Home / SaveForm / UpdateForm 생성
모든 파일 rsf 스니펫으로 생성 해준다.
임포트 해준다.
Form 마지막에 커서를 두고 ctrl + space 하면 임포트를 할 수 있다.
오류 안나고 잘 이동이 됨!
구조
아파치 Nginx의 역할은 파일을 요청받으면 파일을 주는 역할
클라이언트는 포트를 3000번에 요청해서 FS가 받는다.
그리고 백엔드 서버는 8080포트로 받는다.
single페이지 어플리케이션은 최초 / 요청에서 모든 페이지를 다운받아야 되는데,
그 시간이 조금 걸린다.
프론트 서버가 백엔드 서버에 데이터를 요청해서 그림을 그려주는 것이 아니라.
모든 그림 템플릿을 클라이언트에게 최초에 모두 다 주고, 클라이언트가
백엔드 서버로 데이터 요청을 해서 데이터를 받고 그림을 그리게 된다.
그리고 브라우저가 프론트 서버에 요청하면 응답받은 도메인 주소로 다시
백엔드 서버로 요청하게 된다. 이때 가지고 가는 IP가 FS IP이다.
그래서 BS는 FS를 열어주면 된다.
preflight요청으로 CORS설정을 체크하고 확인이되면
API요청을 하게 되는 것
그럼 브라우저로부터 JSON을 받게 되는
package com.cos.jwt.config.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class CorsFilter implements Filter{
public static final String TAG = "MyFilter1 : ";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("CORS 필터 작동");
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "*");
resp.setHeader("Access-Control-Allow-Headers", "*");
// 해당 헤더가 없으면 아래 7가지의 header값만 응답할 수 있다.
// Cache-Control
//Content-Language
//Content-Length
//Content-Type
//Expires
//Last-Modified
//Pragma
resp.setHeader("Access-Control-Expose-Headers", "*");
chain.doFilter(request, response);
}
}
Java
복사
@RequiredArgsConstructor
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter(){
System.out.println("CORS 필터 등록");
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter());
bean.addUrlPatterns("/*");
bean.setOrder(0); // 낮은 번호부터 실행됨.
return bean;
}
}
Java
복사
프록시 패스 되는 과정
어차피 리턴값을 구조분해 할당되는 요소라면 바로 이렇게 넣을 수 있다.
import React from "react";
import { Pagination } from "react-bootstrap";
import PostItem from "../../components/PostItem";
const Home = () => {
function prev() {}
function next() {}
return (
<div>
{<PostItem id={1} title={"제목1"} />}
{<PostItem id={2} title={"제목2"} />}
<br />
<div className="d-flex justify-content-center">
<Pagination>
<Pagination.Item onClick={prev} disabled>
Prev
</Pagination.Item>
<Pagination.Item onClick={next}>Next</Pagination.Item>
</Pagination>
</div>
</div>
);
};
export default Home;
Java
복사
import React from "react";
import { Card } from "react-bootstrap";
import { Link } from "react-router-dom";
const PostItem = ({ id, title }) => {
return (
<Card>
<Card.Body>
<Card.Title>{title}</Card.Title>
<Link to={"/post/" + id} variant="primary" className="btn btn-primary">
상세보기
</Link>
</Card.Body>
</Card>
);
};
export default PostItem;
Java
복사
App.js
Home.js
PostItem.js
Header.js
useDispatch ⇒
userSelector ⇒ 변수를 가져오는 함수
삼항 연산자로 로긴했을 때 보여줘야 될것
그리고 로그인이 안되어 있을 대 보여줘야 될 것을 구분해준다.
import React from "react";
import { Nav, Navbar } from "react-bootstrap";
import { useSelector } from "react-redux";
import { Link } from "react-router-dom";
function Header(props) {
const isLogin = useSelector((state) => state.isLogin);
return (
<div>
<Navbar bg="dark" expand="lg" variant="dark">
<Link to="/" className="navbar-brand">
블로그홈
</Link>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
{isLogin ? (
<>
<Link to="/saveForm" className="nav-link">
글쓰기
</Link>
<Link className="nav-link">로그아웃</Link>
</>
) : (
<>
<Link to="/loginForm" className="nav-link">
로그인
</Link>
<Link to="/joinForm" className="nav-link">
회원가입
</Link>
</>
)}
</Nav>
</Navbar.Collapse>
</Navbar>
<br />
</div>
);
}
export default Header;
Java
복사
axios 사용
CORS 필터 (서버쪽에서 설정)
package shop.mtcoding.blog._core.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("CORS 필터 작동");
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "*");
resp.setHeader("Access-Control-Allow-Headers", "*");
// 해당 헤더가 없으면 아래 7가지의 header값만 응답할 수 있다.
// Cache-Control
//Content-Language
//Content-Length
//Content-Type
//Expires
//Last-Modified
//Pragma
resp.setHeader("Access-Control-Expose-Headers", "*");
chain.doFilter(request, response);
}
}
Java
복사
환경변수를 이렇게도 사용가능하다.
변수 가져오기
package shop.mtcoding.blog._core.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import shop.mtcoding.blog._core.filter.CorsFilter;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter(){
System.out.println("CORS 필터 등록");
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter());
bean.addUrlPatterns("/*");
bean.setOrder(0); // 낮은 번호부터 실행됨.
return bean;
}
}
Java
복사
Filter를 Component로 띄우는게 중요
FilterConfig 등록
FilterConfig에서 corsFilter를 new로 넣으면
환경변수를 끌어다 쓰는 corsFilter가 적용이 안된다.
그래서 corsFilter를 의존성 주입으로해서 사용해야지 된다.
import axios from "axios";
import React, { useEffect, useState } from "react";
import { Pagination } from "react-bootstrap";
import PostItem from "./../../components/PostItem";
const Home = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
apiHome();
}, []);
async function apiHome() {
let response = await axios({
url: "http://localhost:8080",
method: "get",
});
console.log("posts", response.data);
setPosts(response.data.body);
}
function prev() {}
function next() {}
return (
<div>
{posts.map((post) => (
<PostItem id={post.id} title={post.title} />
))}
{/* {<PostItem id={1} title={"제목1"} />} */}
<br />
<div className="d-flex justify-content-center">
<Pagination>
<Pagination.Item onClick={prev} disabled>
Prev
</Pagination.Item>
<Pagination.Item onClick={next}>Next</Pagination.Item>
</Pagination>
</div>
</div>
);
};
export default Home;
Java
복사
List는 리스트에서 동일한 속성이면
순서가 바뀌어도 순서가 바뀐줄 인식을 못한다.
그래서 key가 필요
상태관리하는 곳에서는
컬렉션을 다룰 때 키를 줘야 된다.