const one = () => Promise.resolve('ONE');
async function myFunc(){
console.log('In Function');
const res = await one();
console.log(res)
}
console.log('Before Func');
await myFunc();
console.log('After Func');
[변환]
const one = () => Promise.resolve('ONE');
async function myFunc(){
console.log('In Function');
return one().then(res => {
console.log(res)
})
}
console.log('Before Func');
myFunc().then(() => {
console.log('After Func');
})
const {createProxyMiddleware} = require("http-proxy-middleware");
module.exports = function(app){
app.use("/api", createProxyMiddleware({ target: '', changeOrigin: true}))
}
import React, { useState, useCallback } from 'react';
function InputComponent() {
// 상태 초기화
const [inputValue, setInputValue] = useState('');
// 입력값을 업데이트하는 함수 (useCallback 사용)
const handleInputChange = useCallback((event) => {
setInputValue(event.target.value);
}, []); // 의존성 배열이 비어 있으므로 함수는 한 번만 생성됨
return (
<div>
<input
type="text"
value={inputValue} // input의 값은 상태에 바인딩
onChange={handleInputChange} // input의 변화가 있을 때 상태를 업데이트
/>
<p>입력값: {inputValue}</p>
</div>
);
}
export default InputComponent;
[store.js]
const initialState = {
number: 0
};
function reducer(state, action) {
switch (action.type) {
case 'ADD':
return {
number: state.number + 1
}
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState)
const onAdd = () => {
dispatch({type: 'ADD'})
}
return (
<div>
<h1>{state.number}</h1>
<button onClick={onAdd}>Add</button>
</div>
)
}
ex)
[개선전]
const PrimeNumbers = ({ max}) => {
const primes = calculatePrimeNumbers(max);
return(
<div>
<p>{primes.join(', ')} </p>
</div>
)
};
const App = () => {
const [max, setMax] = useState(10);
return(
<div>
<PrimeNumbers max={max} />
</div>
)
}
[개선후]
const PrimeNumbers = ({ max}) => {
const primes = useMemo(() => calculatePrimeNumbers(max));
return(
<div>
<p>{primes.join(', ')} </p>
</div>
)
};
const App = () => {
const [max, setMax] = useState(10);
return(
<div>
<PrimeNumbers max={max} />
</div>
)
}
ex)
const TestComponent = () => {
const [number, setNumber] = useState(0);
const increaseNumber = useCallback(() => {
number++;
}, [number]);
return (
<div>
<button onClick = {increaseNumber}> + </button>
</div>
)
}
[개선전]
const ChildComponent = ({ count }) => {
return <div>Count: {count}</div>
};
const ParentComponent = () => {
const [count, setCount] = useState(0);
const [inputValue, setInputValue] = useState('')
return(
<div>
<button onClick={() => setCount(count +1)}>증가</button>
<input
type="text"
value={inputValue}
onChange= {(e) => setInputValue(e.target.value)}
/>
<ChildComponent count={count} />
</div>
)
};
[개선후]
const ChildComponent = React.memo(({ count }) => {
return <div>Count: {count}</div>
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
const [inputValue, setInputValue] = useState('')
return(
<div>
<button onClick={() => setCount(count +1)}>증가</button>
<input
type="text"
value={inputValue}
onChange= {(e) => setInputValue(e.target.value)}
/>
<ChildComponent count={count} />
</div>
)
};
1) API통신
2) ref를 이용한 dom제어
3) window 전역 객체 이벤트 리스너 할당/해제
4) 예시 :
[훅]
import { useState, useEffect } from 'react';
// 데이터 로딩 훅
function useDataFetching(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]); // url이 바뀔 때마다 데이터를 다시 fetch
return { data, loading, error };
}
// 윈도우 크기 감지 훅
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener('resize', handleResize);
// cleanup 함수: 이벤트 리스너를 제거
return () => window.removeEventListener('resize', handleResize);
}, []); // 처음 렌더링 시 한 번만 실행
return size;
}
function useCustomHook(url) {
const { data, loading, error } = useDataFetching(url);
const size = useWindowSize();
return { data, loading, error, size };
}
export default useCustomHook;
[컴포넌트]
import React from 'react';
import useCustomHook from './useCustomHook';
function App() {
const { data, loading, error, size } = useCustomHook('https://jsonplaceholder.typicode.com/posts');
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<h1>Data:</h1>
<ul>
{data.slice(0, 5).map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
<h2>Window Size:</h2>
<p>Width: {size.width}px</p>
<p>Height: {size.height}px</p>
</div>
);
}
export default App;
import React, { useState, useEffect, useCallback } from 'react';
const MyComponent = () => {
const [count, setCount] = useState(0);
const [isVisible, setIsVisible] = useState(true);
// useCallback을 사용하여 함수 메모이제이션
const handleClick = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []); // 의존성이 빈 배열이므로 컴포넌트가 마운트될 때 한 번만 생성됩니다.
const toggleVisibility = useCallback(() => {
setIsVisible((prev) => !prev);
}, []); // 의존성이 빈 배열이므로 컴포넌트가 마운트될 때 한 번만 생성됩니다.
useEffect(() => {
// handleClick 함수를 사용할 때마다
console.log('handleClick has been called');
}, [handleClick]); // handleClick이 변경될 때마다 실행됩니다.
useEffect(() => {
// toggleVisibility 함수를 사용할 때마다
console.log('toggleVisibility has been called');
}, [toggleVisibility]); // toggleVisibility가 변경될 때마다 실행됩니다.
return (
<div>
<button onClick={handleClick}>Increment</button>
<button onClick={toggleVisibility}>Toggle Visibility</button>
<div>{isVisible ? 'Visible' : 'Hidden'}</div>
<div>Count: {count}</div>
</div>
);
};
export default MyComponent;
import React, { useReducer } from 'react';
// 초기 상태
const initialState = {
isLoggedIn: false,
user: null,
};
// reducer 함수 정의
function reducer(state, action) {
switch (action.type) {
case 'LOGIN':
return { ...state, isLoggedIn: true, user: action.payload };
case 'LOGOUT':
return { ...state, isLoggedIn: false, user: null };
default:
return state;
}
}
function UserProfile() {
const [state, dispatch] = useReducer(reducer, initialState);
const handleLogin = () => {
const user = { name: 'John Doe', age: 30 }; // 예시 사용자 정보
dispatch({ type: 'LOGIN', payload: user });
};
const handleLogout = () => {
dispatch({ type: 'LOGOUT' });
};
return (
<div>
{state.isLoggedIn ? (
<div>
<h1>Welcome, {state.user.name}</h1>
<p>Age: {state.user.age}</p>
<button onClick={handleLogout}>Logout</button>
</div>
) : (
<div>
<h1>Please log in</h1>
<button onClick={handleLogin}>Login</button>
</div>
)}
</div>
);
}
export default UserProfile;
이전버전
import React, { forwardRef } from 'react';
const MyButton = forwardRef((props, ref) => (
<button ref={ref} {...props}>
{props.children}
</button>
));
// 사용 예시
const App = () => {
const buttonRef = React.useRef();
return <MyButton ref={buttonRef}>Click Me</MyButton>;
};
React19에서 변화
import React, { forwardRef } from 'react';
type MyComponentProps = {
ref?: React.Ref<HTMLDivElement>;
children: React.ReactNode;
};
const MyButton = ({ ref, ...props }:MyComponentProps) => {
return (
<button ref={ref} {...props}>
{props.children}
</button>
);
};
// 사용 예시
const App = () => {
const buttonRef = React.useRef();
return <MyButton ref={buttonRef}>Click Me</MyButton>;
};
특징 | Context API | Redux |
---|---|---|
설정 | 간단하게 설정 가능 | 설정이 복잡하고 보일러플레이트가 많음 |
상태 관리 범위 | 주로 작은 애플리케이션에서 전역 상태 관리 | 대규모 애플리케이션에서 복잡한 상태 관리 |
성능 | 상태가 커지면 성능 저하 가능 | 성능 최적화가 잘 되어 있고 대규모 애플리케이션에서 안정적 |
미들웨어 | 미들웨어 없음 | 미들웨어(예: Redux Thunk, Saga 등) 지원 |
사용 예 | 간단한 상태 공유 (예: 테마, 로그인 상태 등) | 복잡한 상태 관리가 필요한 대규모 애플리케이션 |
[action.js]
// actions.js
export const fetchDataRequest = () => ({
type: 'FETCH_DATA_REQUEST',
});
export const fetchDataSuccess = (data) => ({
type: 'FETCH_DATA_SUCCESS',
payload: data,
});
export const fetchDataFailure = (error) => ({
type: 'FETCH_DATA_FAILURE',
payload: error,
});
// 비동기 API 호출을 처리하는 액션 (redux-thunk 사용)
export const fetchData = () => {
return (dispatch) => {
dispatch(fetchDataRequest()); // 요청 시작 액션
fetch('https://jsonplaceholder.typicode.com/posts') // 예시 API 호출
.then((response) => response.json())
.then((data) => dispatch(fetchDataSuccess(data))) // 성공 시 데이터 저장
.catch((error) => dispatch(fetchDataFailure(error))); // 실패 시 에러 처리
};
};
[reducer.js]
// reducer.js
const initialState = {
loading: false,
data: [],
error: '',
};
const dataReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_DATA_REQUEST':
return { ...state, loading: true };
case 'FETCH_DATA_SUCCESS':
return { ...state, loading: false, data: action.payload };
case 'FETCH_DATA_FAILURE':
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
export default dataReducer;
[store.js]
// store.js
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import dataReducer from './reducer';
import thunk from 'redux-thunk';
// 미들웨어 적용
const store = createStore(dataReducer, applyMiddleware(thunk));
export default store;
[App.js]
// App.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchData } from './actions'; // 비동기 액션 임포트
const App = () => {
const dispatch = useDispatch();
const { loading, data, error } = useSelector((state) => state);
// useEffect를 사용하여 초기 데이터 요청
useEffect(() => {
dispatch(fetchData()); // 컴포넌트가 마운트될 때 데이터 로드
}, [dispatch]);
return (
<div>
<h1>Data Fetch Example</h1>
<button onClick={() => dispatch(fetchData())}>Fetch Data</button>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
<ul>
{data && data.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</div>
);
};
export default App;
import React, { createContext, useContext } from 'react';
// Context 생성
const UserContext = createContext();
// 최상위 컴포넌트
function App() {
const user = { name: 'John Doe', age: 30 };
return (
<UserContext.Provider value={user}>
<h1>App Component</h1>
<Parent />
</UserContext.Provider>
);
}
// 부모 컴포넌트
function Parent() {
return (
<div>
<h2>Parent Component</h2>
<Child />
</div>
);
}
// 자식 컴포넌트
function Child() {
return (
<div>
<h3>Child Component</h3>
<Grandchild />
</div>
);
}
// 손자 컴포넌트
function Grandchild() {
// Context에서 user 데이터 가져오기
const user = useContext(UserContext);
return (
<div>
<h4>Grandchild Component</h4>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
);
}
export default App;
import React from 'react';
// 최상위 컴포넌트
function App() {
const user = { name: 'John Doe', age: 30 };
return (
<div>
<h1>App Component</h1>
<Parent user={user}>
{/* 중간 컴포넌트에 user 데이터를 children으로 전달 */}
<Child />
</Parent>
</div>
);
}
// 부모 컴포넌트
function Parent({ user, children }) {
return (
<div>
<h2>Parent Component</h2>
{/* 자식 컴포넌트를 렌더링하면서 children으로 전달된 컴포넌트에게 user 데이터를 props로 전달 */}
{React.cloneElement(children, { user })}
</div>
);
}
// 자식 컴포넌트
function Child({ user }) {
return (
<div>
<h3>Child Component</h3>
<Grandchild user={user} />
</div>
);
}
// 손자 컴포넌트
function Grandchild({ user }) {
return (
<div>
<h4>Grandchild Component</h4>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
);
}
export default App;
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
[자식Component]
<script setup>
const model = defineModel(); // 기본적으로 v-model을 처리
</script>
<template>
<input v-model="model" class="border p-2" />
</template>
[부모Component]
<script setup>
import MyInput from './MyInput.vue'
import { ref } from 'vue'
const name = ref('')
</script>
<template>
<MyInput v-model="name" />
<p>입력한 값: {{ name }}</p>
</template>
[자식]
<script setup>
import { ref } from 'vue'
const inputRef = ref(null)
const focusInput = () => {
inputRef.value?.focus()
}
// 부모에서 사용할 수 있도록 expose!
defineExpose({
focusInput
})
</script>
<template>
<input ref="inputRef" placeholder="Type here..." class="border p-2" />
</template>
[부모]
const inputComponent = ref(null)
onMounted(() => {
// 자식의 focusInput() 메서드 호출!
inputComponent.value.focusInput()
})
</script>
<template>
<MyInput ref="inputComponent" />
</template>
[자식]
<script setup>
// 부모로부터 v-model로 값을 전달받음
const text = defineModel<string>()
// 내부 input 참조
const inputRef = ref<HTMLInputElement | null>(null)
// 부모에서 호출할 수 있게 노출할 메서드
function clear() {
text.value = ''
inputRef.value?.focus()
}
// expose
defineExpose({
clear
})
</script>
<template>
<input
ref="inputRef"
v-model="text"
placeholder="Type something..."
class="border px-2 py-1 rounded"
/>
</template>
[부모]
<script setup>
const message = ref('')
const textInputRef = ref(null)
// 버튼 클릭 시 자식의 clear() 호출
const clearText = () => {
textInputRef.value?.clear()
}
</script>
<template>
<TextInput v-model="message" ref="textInputRef" />
<p class="mt-2">입력한 메시지: {{ message }}</p>
<button @click="clearText" class="mt-2 px-4 py-1 bg-blue-500 text-white rounded">
Clear from Parent
</button>
</template>
<template>
<KeepAlive>
<component :is="currentComponent" />
</KeepAlive>
<button @click="currentComponent = HelloWorld">Go Hello </button>
<button @click="currentComponent = AbcWorld">Go Abc </button>
</template>
<script setup>
import HelloWorld from '../HelloWorld.vue';
</script>
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
// Component here is NOT lazy loaded
component: HomeView
},
{
path: '/about',
name: 'about',
// Component here is lazy loaded
component: () => import('../views/AboutView.vue')
},
{
path: '/contact',
name: 'contact',
// Component here is lazy loaded
component: () => import('../views/ContactView.vue')
}
]
})
export default router
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...서버에서 컴포넌트를 로드하는 로직
resolve(/* 로드 된 컴포넌트 */)
})
})
// ... 일반 컴포넌트처럼 `AsyncComp`를 사용
const AsyncComp = defineAsyncComponent({
// 로더 함수
loader: () => import('./Foo.vue'),
// 비동기 컴포넌트가 로드되는 동안 사용할 로딩 컴포넌트입니다.
loadingComponent: LoadingComponent,
// 로딩 컴포넌트를 표시하기 전에 지연할 시간. 기본값: 200ms
delay: 200,
// 로드 실패 시 사용할 에러 컴포넌트
errorComponent: ErrorComponent,
// 시간 초과 시, 에러 컴포넌트가 표시됩니다. 기본값: 무한대
timeout: 3000
})
const useUser = () => {
const getUser = async (id: number): Promise<User> => {
return (await axios.get(`/api/users/${id}`)).data;
};
return { getUser };
};
[union type]
type Marvel = "IronMan" | "Hulk";
type Dc = "BatMan" | "SuperMan";
[intersection type]
type FavoriteSport = "swimming" | "football";
type BallSport = "football" | "baseball";
type FavoriteBallSport = FavoriteSport & BallSport; // 'football'
import Image from 'next/image';
function MyComponent() {
return (
<div>
<h1>My Next.js Image Optimization</h1>
<Image
src="/images/my-image.jpg" // 이미지 경로
alt="My Optimized Image" // 이미지 alt 텍스트
width={600} // 이미지 가로 크기
height={400} // 이미지 세로 크기
priority // 페이지 로딩 시 우선적으로 로딩 (필요시 사용)
/>
</div>
);
}
export default MyComponent;
저장소 생성>커미터 추가>개발환경 구축>CI/CD구축>빌드>패키지 저장소에 publish인데
모노레포에서는 저장소 생성 및 커미터 추가과정이 필요없고 개발환경,CI/CD 등도 기존DevOps를 사용하므로 새프로젝트 생성에대한 오버헤드가 없다.