ajax: JS에서 제공해주는 비동기 통신방식
JSON: 객체 표현식
모든 입출력 데이터는 문자열이다 !
chrome 확장 프로그램 JSON Formatter 설치
JavaScript 환경에서 HTTP 요청을 보내고 응답을 처리하는 데 사용되는 Promise 기반의 HTTP 클라이언트 라이브러리
요청 데이터를 JSON으로 직렬화하거나, 응답 데이터를 JSON으로 자동 파싱
간단한 사용법과 강력한 기능을 제공하여 RESTful API와의 상호작용에 널리 활용
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> // cdn
<script>
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
// 성공했을 때
console.log(res.data);
// title만 콘솔에 출력
res.data.forEach(d => console.log(d.title));
console.log("----------------------");
res.data.map(d => console.log(d.title));
})
.catch(err => {
// 실패했을 때
console.log(err);
})
</script>
</head>
<body>
</body>
</html>
axios
.post("https://jsonplaceholder.typicode.com/posts", {
title: "axios post test",
body: "axios is fun!!!",
userName: "홍길동"
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
})
요청 본문에 적은 건 객체로 보냈는데 네트워크 탭 > 페이로드 > 파싱된 데이터 보면
이렇게 axios가 자동으로 JSON으로 파싱해주었다!
모든 요청 전에 선실행되는 것 !!
요청할 때마다 서버는 Authorization 헤더값을 보고 판단한다.
매번 붙여서 서버에 요청하기엔 번거로우니 인터셉터를 사용해 편하게 쓰자!
axios.interceptors.request.use(config => {
config.headers.Authorization = `Bearer MY_ACCESS_TOKEN`;
return config;
}, error => {
return Promise.reject(error);
});
이런식으로 axios 요청하기 전에 인터셉터를 만들어두면
이런식으로 헤더에 자동으로 들어간다.
위 코드에서 request 대신 response 로 바꿔주면 된다.
axios.interceptors.response.use(response => {
console.log('Interceptor response data', response.data);
return Promise.resolve(response);
}, error => {
return Promise.reject(error);
});
전체
// 요청 인터셉터
axios.interceptors.request.use(config => {
config.headers.Authorization = `Bearer MY_ACCESS_TOKEN`;
return config;
}, error => {
return Promise.reject(error);
});
// 응답 인터셉터
axios.interceptors.response.use(response => {
console.log('Interceptor response data', response.data);
return Promise.resolve(response);
}, error => {
return Promise.reject(error);
});
// post
axios
.post("https://jsonplaceholder.typicode.com/posts", {
title: "axios post test",
body: "axios is fun!!!",
userName: "홍길동"
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
// get
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
// 성공했을 때
console.log(res.data);
})
.catch(err => {
// 실패했을 때
console.log(err);
})
GET: 200 OK
POST: 201 Created
국가명, 국기 이미지, 지도 링크 등을 출력해보자 !!
<!-- axios, jquery 라이브러리 추가 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.js"
integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous">
</script>
axios.get("https://restcountries.com/v3.1/all")
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- axios, jquery 라이브러리 추가 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.js"
integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous">
</script>
<script>
axios.get("https://restcountries.com/v3.1/all")
.then(res => {
console.log(res);
res.data.forEach(country => {
// console.log(country.name.official);
// console.log(country.flags.png);
// console.log(country.maps.googleMaps);
// console.log(country.maps.openStreetMaps);
// console.log("---------------------");
const li = `
<li>
<img src="${country.flags.png}" alt="${country.flags.alt}" />
<p>${country.name.common} (${country.name.official})</p>
</li>
`;
$('ul').append(li);
});
})
.catch(err => {
console.log(err);
});
</script>
<style>
img { width: 100px; height: auto; }
p { display: inline; }
</style>
</head>
<body>
<h1>국가 정보를 가져와서 출력</h1>
<ul>
<!--
<li>
<img src="" alt="국기설명" />
<p>일반국가명 (공식국가명)</p>
</li>
-->
</ul>
</body>
</html>
<!-- Bootstrap JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous">
</script>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
container 는 기본 레이아웃 중앙으로 잡아주는 것 !
row row-cols-1 row-cols-md-2 row-cols-lg-3 ← 반응형 된다,, 대박
g-3 는 gap 이다.
img-fluid ⬇️
Bootstrap의 이미지는 .img-fluid를 통해서 반응형으로 만들어집니다. 이렇게 하면 이미지에 max-width: 100%; 및 height: auto;가 적용되어 부모 요소와 함께 크기가 조정됩니다.
ms(margin-start) 는 margin-left
me(margin-end) 는 margin-right
ms/me cf
부트스트랩 말로만 들어보고 처음 써보는데 되게 간편한 것 같다!!!
여기까지 전체 (국가 정보 GET 및 부트스트랩으로 디자인 적용)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- axios, jquery 라이브러리 추가 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.js"
integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous">
</script>
<!-- Bootstrap JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous">
</script>
<script>
axios.get("https://restcountries.com/v3.1/all")
.then(res => {
console.log(res);
res.data.forEach(country => {
const li = `
<li class="d-flex align-items-center border p-3 bg-white rounded shadow-sm">
<img src="${country.flags.png}" alt="${country.flags.alt}" class="img-fluid rounded" />
<p class="mb-0 ms-3">${country.name.common} (${country.name.official})</p>
</li>
`;
$('ul').append(li);
});
})
.catch(err => {
console.log(err);
});
</script>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
img { width: 100px; height: auto; margin-right: 10px; }
ul { padding: 0; }
li { list-style-type: none; margin-bottom: 20px; }
</style>
</head>
<body class="bg-light">
<div class="container my-5">
<h1 class="text-center text-primary mb-4">국가 정보를 가져와서 출력</h1>
<ul class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3"></ul>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- axios, jquery 라이브러리 추가 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.js"
integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous">
</script>
<!-- Bootstrap JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous">
</script>
<script>
// 서버로 부터 가져온 전체 국가 정보를 저장할 배열
let datas = [];
// 국가 정보를 담고 있는 배열을 출력
const showCountryInfo = countryInfos => {
$('ul').empty();
countryInfos.forEach(country => {
const li = `
<li class="d-flex align-items-center border p-3 bg-white rounded shadow-sm">
<img src="${country.flags.png}" alt="${country.flags.alt}" class="img-fluid rounded" />
<p class="mb-0 ms-3">${country.name.common} (${country.name.official})</p>
</li>
`;
$('ul').append(li);
});
};
axios.get("https://restcountries.com/v3.1/all")
.then(res => {
datas = [...res.data];
showCountryInfo(res.data);
})
.catch(err => {
console.log(err);
});
</script>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
img { width: 100px; height: auto; margin-right: 10px; }
ul { padding: 0; }
li { list-style-type: none; margin-bottom: 20px; }
</style>
</head>
<body class="bg-light">
<div class="container my-5">
<h1 class="text-center text-primary mb-4">국가 정보를 가져와서 출력</h1>
<div class="d-flex justify-content-center mb-4">
<input type="text" class="form-control" placeholder="검색할 국가명을 입력하세요." />
</div>
<ul class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3"></ul>
</div>
<script>
$('input').on('keyup', e => {
const inputText = $(e.currentTarget).val().toLowerCase();
console.log(inputText); // 현재 입력창에 입력된 내용(글자)
// 서버로부터 가져온 국가 정보에서 국가명에 입력창의 내용이 포함된 건만 추출해서 출력
// ~~~~~~~~~~~~~~~~~~~~ ~~~~ ~~~~~~~~~~ ~~~~~~~~~~~~~ ~~~
// datas datas[*].name.common inputText | showCountryInfo()
// datas[*].name.official <-- Array.filter()
const filteredData = datas.filter(data =>
data.name.common.toLowerCase().indexOf(inputText) >= 0 ||
data.name.official.toLowerCase().indexOf(inputText) >= 0
);
showCountryInfo(filteredData);
})
</script>
</body>
</html>
TODO
- 검색 결과로 조회된 국가 개수를 출력하는 코드를 추가
- 국가명 아래에 구글 지도와 Open Street Map 지도를 보여주는 링크를 추가
(링크를 클릭하면 지도가 새창에서 열리도록)
소프트웨어를 구성하는 하나의 독립적인 단위로, 특정 기능을 캡슐화하여 다른 코드와 분리한 코드의 집합
모듈을 사용하면 코드 재사용성을 높이고, 관리와 유지보수를 쉽게 하며, 복잡한 시스템을 더 작은 단위로 나누어 개발하는 것이 가능
꼭 읽어보기 ! ⬇️
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
import { add, subtract } from './math.js';
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
export default function multiply(a, b) {
return a * b;
}
import multiply from './math.js'; // 중괄호 X
console.log(multiply(5, 3)); // 15
The library for web and native user interfaces = 사용자 인터페이스를 만들기 위한 자바스크립트 라이브러리
라이브러리 ⇒ 자주 사용되는 기능을 정래해 모아 놓은 것
SPA(Single Page Application)를 쉽고 빠르게 만들수 있도록 해 주는 도구
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- #1 HTML 파일에 DOM 컨테이너를 추가
~~~~~~~~~~
Root DOM Node => Virtual DOM의 시작점
-->
<div id="root"></div>
<!-- #2 리액트 파일(react.js, react-dom.js)을 추가
https://ko.legacy.reactjs.org/docs/cdn-links.html
-->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- #3 리액트 컴포넌트 코드를 추가 -->
<script src="MyButton.js"></script>
</body>
</html>
function MyButton(props) {
const [isClicked, setIsClicked] = React.useState(false);
// <button>Clicked</button>
return React.createElement(
"button",
{ onClick: () => setIsClicked(!isClicked) },
isClicked ? "Clicked" : "Click here"
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(React.createElement(MyButton));
너무 복잡슨 ,, JSX 로 쓰자 !!
HTML => XML
~~~~~~~~~~~~~~~~~~~~~
const element = <h1>Hello, World!</h1>; 😇
~~~~~~~~~~~~~~~ ~
| |
<----- JavaScript ----------->
const element = React.creatElement('h1', {}, "Hello, World!"); 😭
리액트 기반 웹 애플리케이션 개발에 필요한 모든 설정 상태의 프로젝트를 만들어 주는 도구
npx create-react-app my-cra-app
해당 폴더로 들어가서
npm start
하니까 무슨 리액트19가 CRA 이제 지원 안 한다고 해서 (한 달도 안 된듯ㅜ)
에러가 떠서 여기 참고해서 해결했다
버전을 낮추고 npm install react@18 react-dom@18
npm i web-vitals
이것까지 설치해주면
npm start
드디어 실행이 잘된당 !!
접속하면 뜨는 페이지 : public/index.html
그곳에 <div id="root"></div>
<= root는 시작되는 위치!
index.js 에서 root id 를 ReactDOM의 Root 로 만들어주는 코드가 있다! ⬇️
const root = ReactDOM.createRoot(document.getElementById('root'));
그리고 root.render 에 App 을 넣어주고 있다. ⬇️
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
그래서 App.js 에 그려준 MyButton 이 보이는 것이다 ! ⬇️
import MyButton from './MyButton';
function App() {
return (
<MyButton />
);
}
export default App;
MyButton.js ⬇️
import { useState } from "react";
function MyButton() {
const [isClicked, setIsClicked] = useState(false);
/*
return React.createElement(
"button",
{ onClick: () => setIsClicked(!isClicked) },
isClicked ? "Clicked" : "Click here"
);
*/
// 훨씬 보기 좋아졌다!
return (
<button onClick={() => setIsClicked(!isClicked)}>
{isClicked ? "Clicked" : "Click here"}
</button>
);
}
export default MyButton;
npx create-next-app
하고
해당 폴더로 들어가서
CNA 에서도 마찬가지로 파일을 넣고 npm run dev
하면 똑같이 뜬다.
차세대 빌드 도구
JavaScript, TypeScript 기반 웹 애플리케이션을 더 빠르고 효율적으로 개발하기 위해 설계
c:\react> npm create vite my-app
~~~~~~
프로젝트 이름
npx
create-vite my-app
√ Select a framework: » React ⇐ 화살표 키를 이용해서 이동 후 선택
√ Select a variant: » JavaScript
c:\react> cd my-app ⇐ 프로젝트 디렉터리로 이동
c:\react\my-app> npm install ⇐ 의존 모듈 설치
c:\react\my-app> npm run dev ⇐ 개발 서버 실행
my-app@0.0.0 dev
vite
VITE v6.0.7 ready in 246 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
공격자가 전달한 스크립트 코드가 사용자 브라우저를 통해서 실행되는 공격 기법
희생자 브라우저에서 공격자가 전달한 스크립트 코드가 실행되면,
http는 stateless 하다.
예를 들어 클라이언트-서버 간 요청-응답을 하고 시간이 지나고 나서 동일한 요청을 했을 때 서버가 같은 클라이언트로부터 왔다는 것을 알 수 없다.
그것을 알 수 있게 해주는 메커니즘이 쿠키이다!
서버가Set-Cookie: role = user;
를 응답 헤더에 담아서 보내준다. 다음에 올 때는 이 값을 가지고 와! 라고 시킨다.
그럼 클라이언트가 저장해놓고 있다가Cookie: role = user;
를 자동으로 갖고 요청을 보내서 그 때 왔던 클라이언트라는 것을 인식시킨다 !!
공격자가 이 저장된 쿠키를 탈취하면 ,,!! 공격자 PC 에서 이 탈취한 쿠키를 가지고 서버에 접근해서 → 서버 망할 수 있다 ☠️☠️☠️☠️☠️
가짜 페이지를 만들어 사용자 입력을 유도해서 입력한 정보를 탈취
해당 PC의 제어권을 탈취해서 원격에서 제어 => BeEf 와 공격 프레임워크를 활용해서 가능
홍길동
글쓰기 페이지 ----------------------------> 글저장
이렇게 하면 텍스트로 인식됨! => <script> alert('xss') </script>
|
고길동 |
글읽기 페이지 <-----------------------------------+
<script> alert('xss') </script>
스크립트 코드가 실행되지 않도록 안전한 형태로 변경(인코딩)해서 저장하거나 응답
이걸 공격자 것만 막는 게 어렵다. 어떻게 구분할까? 내려보내주는 정보를 믿을 수 있나? -> CSP (컨텐츠 보안 정책) ⭐️⭐️⭐️ (브라우저에서 제공)
cf) CORS (교차 출처 리소스 공유) : sop(Same-origin policy: 동일 출처 정책)를 완화해주는 정책
function App() {
return (
<h1>리액트 앱</h1>
<h2>react app</h2> <= JSX 식에는 부모 요소가 하나 있어야 한다.
);
}
export default App;
방법 1. 다른 태그를 추가
근데 필요없는 div 태그 추가로 쓰기 좀 그렇다..
방법 2. Fragment 컴포넌트를 이용 => 불필요한 태그 추가를 방지
<Fragment>
를 사용하면 된다. 근데 import 도 해야하고,, 치기 귀찮다.
방법 3. Fragment를 축약 방식으로 사용 ⭐️
<>~~~~</>
function App() {
const name = "홍길동";
const age = 23;
return (
<>
<h1>{`안녕, ${name}`}</h1>
<h1>안녕, {name}</h1>
<h2>{age + 2}</h2>
</>
);
}
export default App;
<h2>{name === "홍길동" ? "환영합니다." : "환영하지 않습니다."}</h2>
<h2>{name === "홍길동" ? "환영합니다." : null}</h2>
- (1 > 2) & (2 > 3) = false & false => false
두 연산을 다 보고 최종 false 를 결론 낸다.
- (1 > 2) && (2 > 3) => false
앞 연산만 보고 최종 결론을 낸다. => 뒤 연산이 생략(단락)된다.
=> 빠르게 연산 가능
- (1 < 2) | (2 < 3) => true | false => true
- (1 < 2) || (2 < 3) => true
<h2>{name === "홍길동" && "환영합니다."}</h2>
~~~~~~~~~
조건을 만족했을 때 출력할 내용
name === "홍길동"이 true면 환영합니다를 보여주고, false면 띄우지 않는다.
⭐️⭐️⭐️
&& 는 앞에 것이 true면 뒤에 것을 적용!
|| 는 앞에 것이 false면 뒤에 것을 적용!
태그 내에서 style 속성을 이용해서 스타일을 지정하는 방법
<h1 style={{ backgroundColor: "black", color: "yellow", fontSize: 28, padding: '16px' }}>{`안녕, ${name}`}</h1>
바깥 중괄호 : 표현식을 의미
안쪽 중괄호 : 객체를 의미
이렇게도 사용 가능! ⬇️
const myStyle = { backgroundColor: "black", color: "yellow", fontSize: 28, padding: '16px' }
return (<h1 style={myStyle}>{`안녕, ${name}`}</h1>)
function App() {
const name = "홍길동";
const age = 23;
const myStyle = {
h1Style: { backgroundColor: "black", color: "yellow", fontSize: 28, padding: '16px' },
h2Style: { color: 'red' }
};
return (
<>
<h1 style={myStyle.h1Style}>{`안녕, ${name}`}</h1>
<h1>안녕, {name}</h1>
<h2>{age + 2}</h2>
<h2 style={myStyle.h2Style}>{name === "홍길동" && "환영합니다."}</h2>
</>
);
}
export default App;
function Name() {
let name; // undefined
return name || "아무개";
}
function App() {
return (
<>
안녕, <Name />!
</>
);
}
export default App;
App.css ⬇️
.react {
background-color: black;
color: yellow;
font-size: 40px;
padding: 10px;
}
App.js에 App.css import하고 클래스 속성 추가 ⬇️
import './App.css'
function App() {
return (
<div className="react">
홍길동
</div>
);
}
export default App;
내용이 없는 태그의 경우에는 self-closing 태그를 사용
~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
| +-- <input /> <img /> <hr /> <br /> ...
+-- <input> <img> <hr> <br> ...
import './App.css'
function App() {
return (
<input type="text" />
<input type="text"></input>
);
}
export default App;
import './App.css'
// 자바스크립트 주석
/*
자바스크립트 주석
*/
function App() {
// 자바스크립트 주석
/*
자바스크립트 주석
*/
return (
// 태그가 시작되기 전에는 자바스크립트 주석 사용이 가능
/*
자바스크립트 주석
*/
<>
// 태그가 시작되면 주석이 아닌 일반 문자열로 인식
/*
일반 문자열로 해석되어 화면에서 출력
*/
{/* 자바스크립트 표현식으로 주석을 처리 */}
{ // 인라인 주석을 사용할 때는 유의해야 함 (표현식 끝에 }을 사용하면 주석 처리되어 버림)
}
<input type="text" />
<input type="text"></input>
</>
);
}
export default App;
그냥 태그 시작 이후에는 {/* ~~ */}
이거 쓰자
부모(Component)로부터 상속받은 MyApp에서 render()를 재정의해야한다.
import { Component } from "react";
class **MyApp** extends Component {
render() {
return (
<></>
);
}
}
export default **MyApp**;
상속의 개념이 없다.
단순 반환 값이 화면에 출력된다.
function MyApp() {
return (
<></>
);
}
export default MyApp;
const MyApp = function() {
return (
<></>
);
}
export default MyApp;
const MyApp = () => {
return (
<></>
);
}
export default MyApp;