const Body = () => {
const isDesktop = useMediaQuery({ minWidth: 1024 });
return (
<Container>
<Content>
{/* 내용을 이곳에 추가 */}
<h1>내용을 가운데 정렬합니다.</h1>
<p>기타 내용이 들어갑니다.</p>
</Content>
{isDesktop && (
<Content>
{/* 내용을 이곳에 추가 */}
<h1>내용을 가운데 정렬합니다.</h1>
<p>기타 내용이 들어갑니다.</p>
</Content>
)}
</Container>
);
};
데스크톱 컴퓨터, 노트북, 태블릿, 스마트폰 등 다양한 크기와 해상도의 화면을 가진 디바이스를 사용합니다. 반응형 UI를 사용하면 이러한 다양한 화면 크기에 적응하여 사용자 경험을 향상시킬 수 있습니다.
유형 소개
반응형 웹 디자인 vs 적응형 웹 디자인
두 가지 방식이 동일해 보일 수 있지만 그렇지 않습니다. 두 접근 방식은 서로를 보완하므로 옳고 그른 방법은 없습니다. 콘텐츠가 결정하게 하세요.
Flow
화면 크기가 작아짐에 따라 콘텐츠가 세로 공간을 더 많이 차지하기 시작하고 아래쪽의 콘텐츠는 아래로 밀려나는데, 이를 흐름이라고 합니다. 픽셀과 포인트를 사용한 디자인에 익숙하다면 이해하기 어려울 수 있지만 익숙해지면 완전히 이해가 됩니다.
상대적인 요소들 (Relative units)
분기점(Breakpoints)
최댓값과 최솟값 (Max and Min values)
가끔 모바일 웹사이트를 열었는데 이미지가 화면에 꽉 차면서 깨지는 경우가 있습니다.그것은 이미지가 브라우저 넓이보다 작은 데억지로 늘려놓았기 때문이죠.이미지 넓이에 최댓값을 설정해준다면 브라우저 크기가 이미지를 크기를 훨씬 넘어가도 더 이상 커지거나 깨지지도 않을 것입니다.
중첩된 개체
상대적 위치를 기억하시나요? 서로 의존하는 요소가 많으면 제어하기 어렵기 때문에 컨테이너에 요소를 묶으면 훨씬 더 이해하기 쉽고 깔끔하고 정돈된 상태를 유지할 수 있습니다. 이때 픽셀과 같은 정적 단위가 도움이 될 수 있습니다. 픽셀은 로고나 버튼처럼 크기를 조정하고 싶지 않은 콘텐츠에 유용합니다.
모바일 혹은 데스크톱 우선 작업 (Mobile or Desktop first)
모바일에서 데스크톱으로 프로젝트가 반응형 작업을 할 때와 데스크톱에서 모바일로 진행될 때 기술적으로 큰 차이는 없습니다.하지만 모바일에서 먼저 시작할 때 제한이 적고,콘텐츠 배치를 결정할 때 수월하게 할 수 있습니다.그래도 프로젝트별로 다를 수 있으니 프로젝트에 맞춰서 해보시길 권합니다.
웹폰트와 시스템 폰트 (Webfonts vs System fonts)
반응형 웹디자인에서 유동적인 레이아웃을 위해 이미지보다 폰트를 많이 쓰는데요.폰트는 접속 시 내려받아 설치하는 웹폰트와 사용자 기기에 원래 설치된 시스템폰트가 있습니다.웹폰트를 사용하면 웹사이트를 예쁘게 만들 수 있는 것은 사실입니다.하지만 웹폰트를 다양하게 쓰는 것은 그만큼 페이지를 느리게 할 수 있습니다.개인적으로 제목이나 디스플레이를 위한 폰트는 웹폰트로 하고 본문은 시스템폰트를 사용하는 것을 제안합니다.
비트맵 방식과 벡터 방식 (Bitmap images vs Vectors)
여러분이 만든 아이콘이 스큐어모픽 스타일처럼 화려하고 디테일하다면 비트맵 방식(jpg, png, gif등등)을 사용하는 것이 맞습니다.하지만 이런 스타일이 아니라면 벡터방식(svg)을 써보세요.해상도가 높은 기기에서도 선명하게 보일 것입니다.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import type { RootState } from "../store/store";
import shortid from "shortid";
// Define a type for the slice state
interface Todo {
id: string;
title: string;
contents: string;
isDone: boolean;
}
interface Todos {
todos: Todo[];
}
// Define the initial state using that type
const initialState: Todos = {
todos: [],
};
export const todoSlice = createSlice({
name: "todo",
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers: {
addTodo: (
state,
action: PayloadAction<{ title: string; contents: string }>
) => {
const newTodo = {
id: shortid.generate(),
title: action.payload.title,
contents: action.payload.contents,
isDone: false,
};
state.todos.push(newTodo);
},
deleteTodo: (state, action: PayloadAction<string>) => {
state.todos = state.todos.filter((todo) => todo.id !== action.payload);
},
toggleTodo: (state, action: PayloadAction<string>) => {
// Find the todo by its id
const todoToToggle = state.todos.find(
(todo) => todo.id === action.payload
);
// If the todo is found, toggle its 'isDone' property
if (todoToToggle) {
todoToToggle.isDone = !todoToToggle.isDone;
}
},
},
});
export const { addTodo, deleteTodo, toggleTodo } = todoSlice.actions;
// Other code such as selectors can use the imported `RootState` type
export const selectTodo = (state: RootState) => state.todos;
export default todoSlice.reducer;
- 액션은 type 필드를 가진 자바스크립트 객체입니다. 쉽게 생각해서, 어떤 일이 일어났는지를 설명하는 이벤트라고 생각하셔도 무방합니다. 보통, type과 payload 프로퍼티를 가지며 type은 어떤 액션인지를 나타내며, payload는 데이터를 담습니다.
- Dispatch는 Store의 내장 함수 중 하나로 액션 객체를 넘겨줘서 상태를 업데이트하는 이벤트 트리거입니다.
호출되면, Store는 Reducer 함수를 실행시켜 Action 처리 로직이 있다면 참고해 새로운 상태를 만듭니다
redux에서 dispatch는 액션을 reducer로 전달합니다. 즉, state를 업데이트하는 유일한 방법은 store.dispatch 함수를 호출하는 것입니다. 이벤트를 발생시키는 역할을 한다고 생각하시면 됩니다.
Store에 저장하기
import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "../counter/counterSlice";
import todoSlice from "../todos/todoSlice";
export const store = configureStore({
reducer: {
counters: counterSlice,
todos: todoSlice,
},
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
- RootState및 유형을 각 구성 요소로가져올 수 있지만애플리케이션에서 사용할 수 있도록미리 유형화된 버전의및AppDispatch hooks를 만드는 것이 좋습니다.
- 이렇게하므로써, 매번useSelector입력할 필요가 없습니다.(state: RootState)
// store/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
type DispatchFunc = () => AppDispatch;
export const useAppDispatch: DispatchFunc = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
- 실사용
import { useAppDispatch, useAppSelector } from "../store/hooks";
import { decrement, increment, incrementByAmount } from "./counterSlice";
export function Counter() {
const count = useAppSelector((state) => state.counters.value);
const dispatch = useAppDispatch();
이 코드에서는 React의 useState와 useEffect 훅을 사용하여 데이터 가져오기와 UI 렌더링을 관리하고, try-catch를 사용하여 오류 처리를 수행합니다. 이렇게 하면 데이터를 가져올 때의 비동기 문제를 처리하고, UI를 업데이트하거나 오류 메시지를 표시할 수 있게 됩니다.
AJAX는 비동기식 자바스크립트와 XML(Asynchronous JavaScript And XML)의 약자로, 브라우저가 페이지 전체를 새로 고치지 않고도 서버로부터 데이터를 비동기적으로 로드하고 화면을 업데이트하는 기술입니다. 이를 구현하기 위해 JavaScript를 사용하여 서버와 비동기 통신하며, 주로 XML 데이터를 주고받는 기술로 시작했지만 현재는 JSON을 더 많이 사용합니다.
Fetch API: Fetch API는 최신 웹 표준으로, 브라우저에서 제공하는 내장 함수로서 간단하고 강력한 HTTP 요청을 만들 수 있습니다. 주로 Promise를 기반으로 하며, 다음과 같이 사용할 수 있습니다.
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// 데이터 처리 로직
})
.catch(error => {
// 오류 처리 로직
});
axios 라이브러리: axios는 브라우저와 Node.js에서 모두 사용 가능한 HTTP 클라이언트 라이브러리로, 더 다양한 기능과 브라우저 호환성을 제공합니다. axios는 Promise 기반으로 작동하며 사용하기 쉽고, 오류 처리 및 요청 취소와 같은 고급 기능을 제공합니다.
import axios from 'axios';
axios.get('https://api.example.com/data')
.then(response => {
// 데이터 처리 로직
})
.catch(error => {
// 오류 처리 로직
});