Replies: 16 comments 1 reply
-
팀바팀🧐 우리팀의 생각들어가기 전에
도움이 되었던 부분
어려움이나 불편을 겪었던 부분
우리 팀에서 TypeScript를 사용할 때 중요하게 생각하는 부분은?
Component선언 방식
const Component = () => {
return <div>...</div>
}
export default Component; Props
export interface TextProps {
as?:
| 'p'
| 'span'
| 'strong'
| 'small';
size?: Size;
css?: CSSProp;
}
const Text = (props: TextProps) = {
const { as = 'p', size, css } = props;
/* ... */
} Component with Children / Component without Children
import type { PropsWithChildren } from 'react';
const Button = (props: PropsWithChildren<ButtonProps>) => {
const { children, size, type = 'submit', variant, ...rest } = props;
/* ... */
};
const Calendar = () => { /* ... */ } Event
import type { ChangeEventHandler, FormEventHandler } from 'react';
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => { /* ... */ }
const handleSubmit: FormEventHandler<HTMLFormElement> = (e) => { /* ... */ } HooksuseState
const [count, setCount] = useState(0);
const [userName, setUserName] = useState('');
import type { Position } from '...';
const [position, setPosition] = useState<Position>({
row: 0,
column: 0,
}); useContext
interface ModalContextProps {
isModalOpen: boolean;
openModal: () => void;
closeModal: () => void;
}
export const ModalContext = createContext<ModalContextProps>(
{} as ModalContextProps,
); Ref
const timerId = useRef<ReturnType<typeof setTimeout>>();
const ref = useRef<HTMLUListElement>(null); 모듈type 관리 방식네이밍 컨벤션
export interface Schedule {
id: number;
title: string;
startDateTime: YYYYMMDDHHMM;
endDateTime: YYYYMMDDHHMM;
}
export type ScheduleWithoutId = Omit<Schedule, 'id'>;
export type YYYYMMDDHHMM = `${string}-${string}-${string} ${string}:${string}`; 디렉터리 구조
📁 src
ㄴ📁 types
ㄴ🗒️ schedule.ts
ㄴ🗒️ size.ts type import/export
import type { Rulu, Youth, WizardRabbit } from './types';
export interface Rulu {
foo: string;
bar: number;
}
export type Youth = 'foo' | 'bar';
export type WizzardRabbit = 'baz' | 'qux'; APIRequest / Response Type
export const http = {
get: async <T>(url: RequestInfo | URL): Promise<T> => {
const response = await fetch(url, options);
return response.json();
},
};
export const fetchScheduleById = (scheduleId: number) => {
return http.get<Schedule>(
`/api/team-place/${teamPlaceId}/calendar/schedules/${scheduleId}`,
);
}; 빌드 설정loader
tsconfig{
"compilerOptions": { //어떻게 컴파일 할건지
/* Language and Environment */
"target": "esnext" //컴파일할 자바스크립트 버전. esnext는 가장 최신버전
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
] //컴파일에 포함될 라이브러리 파일 목록을 지정한다.
"jsx": "react-jsx" // jsx 코드를 어떻게 생성하는지 지정한다.
"sourceMap": true, // js파일을 생성할 때 해당 파일과 연결된 소스 맵 파일을 생성한다.
/* Modules */
"module": "esnext" // 컴파일된 JavaScript 코드가 어떤 모듈 시스템을 사용할지 지정한다.
"moduleResolution": "node" // 모듈 해석 방법을 결정한다. CommonJS 모듈 형식과 Node.js의 모듈 해석 규칙을 따른다.
"baseUrl": "." //상대적인 경로를 해석하는 기준이 되는 기본 디렉터리를 지정한다.
"paths": {
"~/*": ["src/*"]
} // baseUrl을 기준으로 관련된 위치에 모듈 이름의 경로 매핑 목록을 나열한다. 절대 경로 설정을 한다.
"resolveJsonModule": true // .json 확장자로 import된 모듈을 포함한다. mock data를 사용할 예정이기 때문에 추가했다.
/* JavaScript Support */
"allowJs": true // JavaScript 파일의 컴파일을 허용한다. .js도 typescript가 컴파일 대상으로 인식
/* Interop Constraints */
"esModuleInterop": true // 모듈 가져오기/내보내기 작업을 더 편리하게 처리한다. jest.config파일이 모듈이기 때문에 true로 설정한다.
"forceConsistentCasingInFileNames": true // 동일 폴더 내 파일의 이름의 대소문자가 정확히 일치하게하는 설정이다.
/* Type Checking */
"strict": true // 타입체크와 코드 검사를 엄격하게 하도록 한다.
"noImplicitAny": true // 암시적 any에 오류를 발생시킨다.
"noFallthroughCasesInSwitch": true // switch문의 case 절에서 명시적인 종료문을 작성하지 않으면 컴파일 오류가 발생한다.
/* Completeness */
"skipLibCheck": true //모든 선언 파일(*.d.ts)의 타입 검사를 건너뛴다.
},
"include": ["src"] //컴파일 대상으로 포함할 파일이나 폴더를 지정한다."src" 폴더 내에 있는 TypeScript 파일들을 컴파일 대상으로 인식한다.
} 🔗 참고한 자료[우리 팀의 우아한 타입스크립트 컨벤션 정하기 여정 | 우아한형제들 기술블로그](https://techblog.woowahan.com/9804/) |
Beta Was this translation helpful? Give feedback.
-
🧐 보투게더팀의 생각들어가기 전에Component선언 방식function MyComponent() {
return ...
}
Props
//interface case
export interface SelectProps<T extends string>
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
selectedOption: string;
optionList: Record<T, string>;
handleOptionChange: (option: T) => void;
isDisabled?: boolean;
}
export default function Select<T extends string>({
selectedOption,
optionList,
handleOptionChange,
isDisabled = false,
...rest
}: SelectProps<T>) { //inline case
export default function WideHeader({ handleLogoClick }: { handleLogoClick: () => void }) { Component with Children / Component without Children
Event Handler vs Eventconst handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => {/*...*/ };❌
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event,option:string) => {/*...*/ };❌
// vs
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {/*...*/ }; ✅
const handleClick = (event: React.MouseEvent<HTMLButtonElement>, option : string) => {/*...*/ }; ✅
import 방식
Hooks기본 훅
// useState 예시
// 타입과 초기값을 지정하는 방식
const [user, setUser] = useState<User | undefined>(undefined); ❌
const [user, setUser] = useState<User | null>(null); ✅
const [user, setUser] = useState<User>({ ✅
id: 0,
nickname: '',
}); Ref• 사용 용도에 따라 element를 특정하는 용도가 아닌 경우 변수의 타입과 동일한 초기값을 설정하고, element를 특정하고자 하는 경우 타입을 지정하고 초기값을 null로 설정합니다. const BackDropRef = useRef<HTMLDivElement>(null); ✅
const countRef = useRef(0); ✅
const BackDropRef = useRef<HTMLDivElement | null>(null); ❌ type 관리 방식
type import/exportimport type { Post } from "@types/post";
export interface PostList {
pageNumber: number;
postList: PostInfo[];
} ✅
export type { PostList }; ❌ APIRequest / Response Type
export interface Category {
id: number;
name: string;
isFavorite: boolean;
}
export interface CategoryResponse {
id: number;
name: string;
favorite: boolean;
} 빌드 설정loader
tsconfig• 설정 기준과 설정한 항목들에 대한 이해 {
"compilerOptions": {
"target": "es2021", // 구형 브라우저는 호환하지 않기로 정했기 때문
"lib": ["dom", "dom.iterable", "esnext"], // 인터넷에서의 환경과, ES6를 사용하기 때문
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true, // strict 관련, noimplicit 관련 모드 전부 적용
"forceConsistentCasingInFileNames": true, // 같은 파일에 대한 일관되지 않은 참조를 허용하지 않을지 여부
"module": "esnext", // 모듈을 위한 코드 생성 설정 (ES6 방식으로 모듈 import/export)
"moduleResolution": "node", // 모듈을 해석하는 방법을 결정하는 옵션
"resolveJsonModule": true, // TypeScript 모듈 내에서 json 모듈을 가져올 수 있도록 함
"isolatedModules": true, // 각 파일을 분리된 모듈로 트랜스파일
"baseUrl": "src",
"paths": {
"@assets/*": ["assets/*"],
"@pages/*": ["pages/*"],
"@components/*": ["components/*"],
"@hooks/*": ["hooks/*"],
"@styles/*": ["styles/*"],
"@utils/*": ["utils/*"],
"@constants/*": ["constants/*"],
"@type/*": ["types/*"],
"@atoms/*": ["atoms/*"],
"@selectors/*": ["selectors/*"],
"@routes/*": ["routes/*"],
"@api/*": ["api/*"],
"@mocks/*": ["mocks/*"]
},
"outDir": "./dist"
},
"include": ["src", "src/custom.d.ts", "__test__", "styled-components.d.ts", "env.d.ts"],
"exclude": ["node_modules"]
}
참고자료[리액트 18의 타입스크립트 타입 변경점](https://blog.shiren.dev/2022-04-28/) [JSX.Element vs ReactNode vs ReactElement의 차이](https://simsimjae.tistory.com/426) [Documentation - TypeScript 3.8](https://www.typescriptlang.org/ko/docs/handbook/release-notes/typescript-3-8.html) [typescript 번들링](https://velog.io/@ginameee/typescript-번들링) [[TypeScript] 컴파일 옵션 살펴 보기 (TSConfig Reference)](https://it-eldorado.tistory.com/128) |
Beta Was this translation helpful? Give feedback.
-
펀잇 🌞🐧🐰🧐 우리팀의 생각들어가기 전에우리팀에서 typescript를 쓸 때 중요하게 생각하는 부분
Component선언 방식
//correct
const Component = () => {...}; Props
//correct
interface HeaderProps {};
//incorrect
interface Props {};
따라서 type vs interface
//correct
type T = 'type' | 'example';
interface SomeThing {};
//incorrect
type T = {}; interface의 경우 확장이 가능하다. Component with Children / Component without Children
Event// correct
const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => { /*...*/ };
// incorrect
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { /*...*/ };
Hooks기본 훅const [data, setUser] = useState<User>();
Refconst ref = useRef<HTMLElement>(null);
모듈type 관리 방식
type import/export//correct
import type { Type } from '@/types/common';
//incorrect
import { Type } from '@/types/commmon';
APIRequest / Response Type
const useCategoryProducts = (categoryId: number, sort: string = PRODUCT_SORT_OPTIONS[0].value) => {
return useGet<CategoryProductResponse>(
() => categoryApi.get({ params: `/${categoryId}/products`, queries: `?page=1&sort=${sort}` }),
[categoryId, sort]
);
}; 빌드 설정loader
tsconfig{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"], //컴파일 과정에서 사용되는 JS API와 같은 라이브러리를 지정
"esModuleInterop": true,
"module": "esnext", //컴파일을 마친 자바스크립트 모듈이 어떤 모듈 시스템을 사용할 지
"moduleResolution": "node", //node전략을 사용하여 모듈 탐색
"resolveJsonModule": true, //json 파일 import 허용
"forceConsistentCasingInFileNames": true, //파일 이름의 대소문자 구분
"strict": true,
"skipLibCheck": true, //라이브러리에 대해서는 타입 체킹x (컴파일 시간 줄일 수 있음)
"jsx": "react-jsx", //tsx를 어떻게 컴파일 할 지 방식을 정함
"outDir": "./dist",
"typeRoots": ["node_modules/@types", "src/types"], //타입의 위치 지정
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"], //컴파일에 포함
"exclude": ["node_modules", "dist"] //컴파일에 제외
} 🔗 참고한 자료https://jobs.class101.net/1dc83442-c2d4-4162-94ae-4d04717f1ae0 |
Beta Was this translation helpful? Give feedback.
-
🧐 집사의고민팀의 생각✅ Component선언 방식
Props
Component with Children / Component without Children
Event
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => { /*...*/ };
// vs
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { /*...*/ };
✅ Hooks기본 훅// useState 예시
// 타입과 초기값을 지정하는 방식
const [data, setUser] = useState<User | undefined>(undefined);
const [data, setUser] = useState<User | null>(null);
const [user, setUser] = useState<User>({});
const [user, setUser] = useState<boolean>(true); 의도한 대로 타입추론이 되는 경우 타입 생략 vs 데이터가 반환되는 훅의 경우 타입 지정하기 Ref
// ref의 용도에 맞게 오버로딩 된 타입을 활용한다
// current를 변경하고 싶은 경우
const ref = useRef() // MutableRefObject
const ref = useRef(3) // MutableRefObject
const ref = useRef<number>() // MutableRefObject ✅
// current를 변경하고 싶지 않은 경우
const ref = useRef<number | null>(null) // RefObject ✅ ✅ 모듈type 관리 방식
📦types
┣ 📂assets
┃ ┗ 📜assets.d.ts
┣ 📂common
┃ ┣ 📜errorBoundary.ts
┃ ┗ 📜utility.ts
┣ 📂food
┃ ┣ 📜client.ts
┃ ┗ 📜remote.ts
┗ 📂review
┃ ┣ 📜client.ts
┃ ┗ 📜remote.ts
type import/export
✅
import type { SomeThing } from "./some-module.js";
export type { SomeThing };
vs
❎
import { SomeThing } from "./some-module.js";
export type SomeThing; // 예시
type Food = {
id: number;
name: string;
imageUrl: string;
purchaseUrl: string;
};
export type { Food }; ✅ APIRequest / Response Type
interface GetSomethingReq {
}
interface GetSomethingRes {
}
✅ 빌드 설정loader
tsconfig
{
"compilerOptions": {
"target": "es6",
"lib": ["dom", "dom.iterable", "ESNext"], // 컴파일에 포함할 표준 라이브러리를 지정합니다 (DOM 관련 API들)
"jsx": "react-jsx",
"module": "ESNext", // 컴파일에 사용할 모듈 시스템을 지정합니다 (ECMAScript Modules)
"moduleResolution": "node", // 모듈 해석 전략을 지정합니다 (Node.js 스타일)
"baseUrl": ".",
"resolveJsonModule": true, // JSON 모듈을 가져오도록 허용합니다
"allowJs": true, // JavaScript 파일도 컴파일하도록 허용합니다
"declaration": true, // .d.ts 형태의 선언 파일을 생성합니다
"outDir": "./lib", // 생성된 .d.ts 선언 파일의 출력 디렉토리를 지정합니다
"noEmitOnError": true, // 오류 발생 시 TypeScript 출력물을 생성하지 않도록 설정합니다
"isolatedModules": true, // 다른 타입의 import를 같은 모듈에서 허용하지 않습니다
"allowSyntheticDefaultImports": true, // 'import x from "module-name"' 형태의 모듈을 허용합니다
"esModuleInterop": true, // ESMInterop을 위해 __importStar와 __importDefault 도우미를 생성합니다
"forceConsistentCasingInFileNames": true, // 파일 이름에서 일관된 대소문자를 강제합니다 (대소문자를 구분하는 경로를 적용합니다)
"strict": true, // 엄격한 타입 체크 옵션을 활성화합니다
"noImplicitAny": true, // 암시적으로 'any' 타입을 허용하지 않습니다
"noFallthroughCasesInSwitch": true, // switch문에서 모든 case에 'break' 또는 'return' 문이 없을 경우 오류를 발생시킵니다
"skipLibCheck": true, // 선언 파일 (*.d.ts)의 타입 체크를 건너뜁니다
"paths": { // 모듈 이름에 대한 경로 매핑 별칭을 지정합니다 (예: "@/*"는 "src/*"를 가리킵니다)
"@/*": ["src/*"]
}
},
"include": ["src"],
"exclude": ["dist", "./src/**/*.stories.tsx"]
} 🧙♂️ 공유하고 싶은 내용
🔗 참고한 자료 |
Beta Was this translation helpful? Give feedback.
-
🧐 하루스터디팀의 생각
들어가기 전에
Component선언 방식
// allowed but does not use
function MyComponent() {
// ...
}
// good
const MyComponent = () => {
// ...
}
Props
type Props = {
// ...
}
const MyComponent = ({ a, b }: Props) => {
// ...
}
Component with Children / Component without Children
type Props = {
// ...
}
const MyComponent = ({ a, b }: React.PropsWithChildren<Props>) => {
// ...
}
Event
import React from "react";
const MyComponent = () => {
// ...
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => { /*...*/ };
// ...
}
Hooks기본 훅
// 초기 상태가 없는 경우
const [data, setUser] = useState<User | null>(null)
// 타입 추론이 가능할 경우
const [count, setCount] = useState(0)
Ref
const ref = useRef<HTMLDivElement>(null) 모듈type 관리 방식1. type vs interface
2. 네이밍 컨벤션
3. 디렉터리 구조
4. 선언 위치
type import/export
import type { Plan, PlanList } from '@Types/study'; APIRequest / Response Type
빌드 설정loader
이후에 추가할 내용
tsconfig
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"target": "esnext", // 컴파일 할 자바스크립트 버전 esnext는 최신버전 (우리는 IE 지원 X)
"jsx": "react-jsx", // tsx 코드를 어떻게 생성할지
"module": "esnext", // 어떤 모듈 시스템을 사용할지
"moduleResolution": "NodeNext", // 컴파일러가 각 import가 어떤 모듈을 가리키는지 해석하는 과정
"esModuleInterop": true, // 모듈 import/export를 좀 더 편하게
"forceConsistentCasingInFileNames": true, // 같은 파일에 대한 일관되지 않은 참조를 허용하지 않을지 여부
"strict": true, // 타입 체크 엄격하게
"skipLibCheck": true, // 라이브러리에 대해서는 type check를 안한다. 컴파일 시간 줄일 수 있음
},
"include": ["src", "__test__"] // 컴파일에 포함
}
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@Components/*": ["./components/*"],
"@Types/*": ["./types/*"],
"@Hooks/*": ["./hooks/*"],
"@Pages/*": ["./pages/*"],
"@Styles/*": ["./styles/*"],
"@Constants/*": ["./constants/*"],
"@Contexts/*": ["./contexts/*"],
"@Assets/*": ["./assets/*"],
"@Utils/*": ["./utils/*"],
"@Apis/*": ["./api/*"]
}
}
} 🧙♂️ 공유하고 싶은 내용
🔗 참고한 자료 |
Beta Was this translation helpful? Give feedback.
-
🧐 행록팀의 생각Component선언 방식const MyComponent = () => { ... };
Propsinterface MyComponentProps {}
Component with Children / Component without Childreninterface MyComponentProps extends PropsWithChildren {}
interface MyComponentProps extends ComponentPropsWithoutRef<'div'> {}
interface MyComponentProps extends ComponentPropsWithRef<'div'> {}
Eventimport type { MouseEvent } from 'react';
const handleClick = (event: MouseEvent<HTMLButtonElement>) => { ... }
Hooks기본 훅const [user, setUser] = useState<User | null>(null);
const [price, setPrice] = useState<Price>({
currency: 'KRW',
amount: 0,
});
const [price, setPrice] = useState<Price>({
currency: 'KRW',
amount: 0,
} ?? initialPrice);
Refconst ref = useRef<HTMLDivElement>(null);
모듈type 관리 방식폴더 구조
네이밍 컨벤션 export type CurrencyType = keyof typeof CURRENCY;
export interface TripData {};
type import/export타입 import import type { ComponentPropsWithoutRef } from 'react';
import type { MyType } from @types/myTypes;
타입 export export type CurrencyType = keyof typeof CURRENCY;
export interface TripData {}; APIRequest / Response Typeimport { END_POINTS } from '@constants/api';
import { axiosInstance } from '@api/axiosInstance';
export interface PatchDayLogItemOrderParams {
tripId: number;
dayLogId: number;
itemIds: number[];
}
export interface PatchDayLogOrderRequestBody {
itemIds: number[];
}
export const patchDayLogItemOrder =
() =>
({ tripId, dayLogId, itemIds }: PatchDayLogItemOrderParams) => {
return axiosInstance.patch<PatchDayLogOrderRequestBody>(
END_POINTS.DAY_LOG_ORDER(tripId, dayLogId),
{
itemIds,
}
);
};
import { END_POINTS } from '@constants/api';
import type { TripData } from '@type/trip';
import { axiosInstance } from '@api/axiosInstance';
export const getTrip = async (tripId: number) => {
const { data } = await axiosInstance.get<TripData>(END_POINTS.TRIP(tripId));
return data;
}; export interface TripData {
id: number;
title: string;
startDate: string;
endDate: string;
description: string | null;
imageUrl: string | null;
cities: CityData[];
dayLogs: DayLogData[];
}
빌드 설정loader
tsconfig{
"compilerOptions": {
"outDir": "./dist",
"target": "ES2015",
"skipLibCheck": true, // 라이브러리 타입 검사를 건너뛰어 컴파일 속도를 단축시키기 위함
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true, // TypeScript 컴파일러가 파일 이름의 대소문자를 일관성있게 관리
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"jsx": "react-jsx",
"allowJs": true,
"baseUrl": ".",
"paths": { // 절대경로 설정
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@type/*": ["./src/types/*"],
"@hooks/*": ["./src/hooks/*"],
"@pages/*": ["./src/pages/*"],
"@styles/*": ["./src/styles/*"],
"@constants/*": ["./src/constants/*"],
"@assets/*": ["./src/assets/*"],
"@api/*": ["./src/api/*"],
"@mocks/*": ["./src/mocks/*"],
"@stories/*": ["./src/stories/*"],
"@router/*": ["./src/router/*"],
"@store/*": ["./src/store/*"],
"@utils/*": ["./src/utils/*"]
},
"jsxImportSource": "@emotion/react", // emotion의 css prop를 사용하기 위함
"allowSyntheticDefaultImports": true // default export 하지 않은 모듈에서도 오류없이 변수로 설정해 import해서 쓸 수 있음
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx"]
} 🧙♂️ 공유하고 싶은 내용공유받고싶습니다 😊 🔗 참고한 자료 |
Beta Was this translation helpful? Give feedback.
-
☕ 요즘카페 프론트엔드 코드컨벤션컨벤션으로 들어가기 전에TypeScript는 우리 팀에 도움이 되고 있나요? 어떤 측면에서 도움이 되고, 혹은 어떤 측면에서는 어려움이나 불편을 겪고 있나요?
Component선언 방식const ExampleComponent = () => { /* ... */ }
Propstype ExampleComponentProps = {
name: string;
description?: string;
};
const ExampleComponent = (props: ExampleComponentProps) => {
const { name, description } = props;
}
Component with Children / Component without Childrentype ExampleComponentProps = PropsWithChildren<{
name: string;
description?: string;
}>;
const ExampleComponent = (props: ExampleComponentProps) => {
const { name, description } = props;
}
Eventtype ExampleComponentProps = {
onClick: React.MouseEventHandler<HTMLButtonElement>;
}
Hooks기본 훅useState<User | null>(null);
useState(true);
RefuseRef<HTMLDivElement>(null);
모듈type 관리 방식type Member = {
name: string;
};
const assign = (member: Member) => {
// ...
}
type import/exportimport type { Cafe } from './types';
export type User = {
name: string;
}
APIRequest / Response Typeclass Client {
getCafes() {
return this.fetchJson<Cafe[]>(`/cafes`);
}
}
빌드 설정loader
tsconfig{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"jsx": "react-jsx", /* Specify what JSX code is generated. */
"module": "commonjs", /* Specify what module code is generated. */
"allowJs": false, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": [
"src/**/*"
]
} |
Beta Was this translation helpful? Give feedback.
-
🤩 셀럽잇들어가기 전에
Component
function App() {
// ...
return <div>...</div>
}
Props
interface ModalContentProps {
isShow?: boolean;
title: string;
children: React.ReactNode;
}
function ModalContent({ isShow = false, title, children }: ModalContentProps) {
return (
<div>
...
</div>
);
} Component with Children / Component without Children
Ref
Event
import 방식
Hooks
const [count, setCount] = useState(0);
const [userName, setUserName] = useState('');
const [data, setUser] = useState<User | null>(null); type 관리방식네이밍 컨벤션
export interface Schedule {
id: number;
title: string;
startDateTime: YYYYMMDDHHMM;
endDateTime: YYYYMMDDHHMM;
}
export type ScheduleWithoutId = Omit<Schedule, 'id'>; type 디렉터리 구조 ├── src
│ ├── @types
│ │ ├── api.types.ts
│ │ ├── celeb.types.ts
│ │ ├── global.d.ts
│ │ ├── map.types.ts
│ │ └── restaurant.types.ts type import/export
export { SomeThing };
import type { SomeThing } from "./some-module.js"; APIapi 자체 타입을 만들고 그 타입을 반환값으로 명시합니다.
EX) 음식점 get 요청 시 받는 형식을 정의 export interface RestaurantListData {
content: RestaurantData[];
currentElementsCount: number;
currentPage: number;
pageSize: number;
totalElementsCount: number;
totalPage: number;
}
export interface RestaurantData {
id: number;
name: string;
category: string;
roadAddress: string;
lat: number;
lng: number;
phoneNumber: string;
naverMapUrl: string;
celebs: { id: number; name: string; youtubeChannelName: string; profileImageUrl: string }[];
images: { id: number; name: string; author: string; sns: string }[];
} 빌드 설정loader
tsconfig.json
|
Beta Was this translation helpful? Give feedback.
-
🧐 동글 팀의 생각컨벤션으로 들어가기 전에
Component선언 방식(함수 선언문 vs 표현식)
const Component = () => { return ... };
export default Component;
Propstype vs interface vs inline type GetWritingResponse = {
id: number;
title: string;
content: string;
};
// styled-components
const LeftSidebarSection = styled.section<{ isLeftSidebarOpen: boolean }>`
display: ${({ isLeftSidebarOpen }) => !isLeftSidebarOpen && 'none'};
`,
Component with Children / Component without ChildrenVFC, FC, PropsWithChildren
EventEvent Handler vs Event const removeTag: MouseEventHandler<HTMLButtonElement> = (event) => { ... }; 이벤트 핸들러 이름은 내부 동작을 잘 나타내도록 짓기로 했기 때문에(ex. removeTag) 이벤트 핸들러임을 명시적으로 나타내주기 위해 Event Handler를 사용한다.
Hooks기본 훅const [data, setUser] = useState<User | null>(null);
const [writingId, setWritingId] = useState<number | null>(null);
const [nickname, setNickname] = useState<string | null>(null); string이나 number같이 빈 값을 나타내는 것을 명시적으로 할 때 null이 없으면 애매하다. Ref
const inputRef = useRef<HTMLInputElement>(null);
const count = useRef<number>(); 참고( interface MutableRefObject<T> {
current: T;
}
interface RefObject<T> {
readonly current: T | null;
} 모듈type 관리 방식
type Props = {
...
};
// BAD 👎
const customFetch = <T, U>(url: T, options: U) => { ... }
// GOOD 👍
const customFetch = <URL, OPTIONS>(url: URL, options: OPTIONS) => { ... }
// BAD 👎
type ApiType = { ... }
// GOOD 👍
type Api = { ... }
type import/export
APIRequest / Response Type
export type GetWritingResponse = {
id: number;
title: string;
content: string;
};
export type GetWritingPropertiesResponse = {
createdAt: Date;
isPublished: boolean;
publishedAt: Date;
publishedTo: Blog;
}; 빌드 설정loader
tsconfig
{
"compilerOptions": {
"baseUrl": "./src", // 모듈 이름을 해석하기 위한 기본 디렉터리
"paths": { // 절대 경로 설정
"*": ["*"]
},
"target": "ES2021", // ES2021 버전에 대한 코드를 생성
"lib": ["DOM", "DOM.Iterable", "ESNext"], // 컴파일러가 포함해야 하는 라이브러리 목록
"jsx": "react-jsx", // JSX 처리 방식
"module": "ESNext", // 사용할 모듈 시스템
"moduleResolution": "Node", // 모듈 해석 방식
"sourceMap": true, // 소스 맵 생성 여부
"esModuleInterop": true, // CommonJS와 ES Modules간의 호환성 설정
"forceConsistentCasingInFileNames": true, // 파일 이름의 대소문자 일관성을 강제하는 설정
"strict": true, // 엄격한 타입 체크 옵션 설정
"noImplicitAny": true, // 암시적인 'any' 타입에 오류를 발생
"skipLibCheck": true // 타입 체킹 시에 .d.ts 파일을 건너뜀
},
"include": ["src", "__tests__"], // 컴파일할 파일 또는 디렉토리 목록
"exclude": ["node_modules"] // 컴파일에서 제외할 파일 또는 디렉토리 목록
} |
Beta Was this translation helpful? Give feedback.
-
🚗 카페인 팀 ⚡️Component
Hooksconst [station, setStation] = useState<Station>(null); 위 방식을 사용한다. 이유: 유니온 타입을 항상 명시하면 너무 코드가 지저분하다. 어차피 동일한 동작을 하므로 위와 같이 사용한다. 모듈
API
빌드 설정ts loader vs babel loader 무엇을 선택하든 단점을 보완할 수 있는 플러그인(?)을 설치하면 돼서 뭘 선택해야할지 모르겠음. ts loader 자체적인 타입 체킹 vs esbuild loader 빌드 속도 빠름 vs babel loader 가장 많이 사용하고 빌드 속도도 캐싱함으로써 esbuild 보다 빠르게 만들 수 있음 |
Beta Was this translation helpful? Give feedback.
-
코끼리끼리팀의 생각 🐘Component선언 방식화살표함수로 선언, exrpot deault로 내보내기 Props
const Component = ({ props1, props2, props3 }) => {}
const Component = (props) => {
const { props1, props2, props3, props4 } = props;
} Component with Children / Component without Children <Component>asdasd</Component>
<Component />
(props):withChildren
const Component = ({children}) => {
<div>{children}</div>
}
type ComponentProps = {
props:any;
} & WidhChildren; Event이벤트 함수에 Handler 타입을 붙이는 것이 아닌, event에 타입 지정 Hooks기본 훅useEffect는 모든 함수를 다 선언한 후에 실행한다 모듈Component만 export default. 그 외의 것들은 설령 내보낼 것이 하나만 있더라도 export 구문을 사용한다 type 관리 방식
type import/export
API생성한 client instance를 사용, 도메인별 데이터 fetching 로직과 fetching 한 데이터의 상태를 관리하는 로직을 분리 // api/client
const getProfile = () => {
return client.get(`/profile/${id}`)
}
// hooks/query/user
const useProfileQuery = () => {
const { data } = useQuery(profileKey, getProfile);
} |
Beta Was this translation helpful? Give feedback.
-
🧐 피움팀의 생각들어가기 전에TypeScript는 우리 팀에 도움이 되고 있나요? 어떤 측면에서 도움이 되고, 혹은 어떤 측면에서는 어려움이나 불편을 겪고 있나요?
우리 팀에서 TypeScript를 사용할 때 중요하게 생각하는 부분은?
Component선언 방법화살표 함수를 이용한 표현식으로 작성합니다. Props
Component with Children / Component without Children
Event
Hooks기본 훅1. const [data, setUser] = useState<User | undefined>(undefined);
2. const [data, setUser] = useState<User | null>(null);
3. const [user, setUser] = useState<User>({});
Ref아직 ref를 사용하지 않았지만 사용하게 된다면 훅과 비슷하게 가지 않을까 싶어요 모듈type 관리 방식
type import/export
APIRequest/Response Type
빌드 설정loader
tsconfig{
"compilerOptions": {
"target": "ES2016", // 인터넷 익스플로러는 지원하지 않음, IE를 지원하지 않으면서 모든 브라우저 지원이 가능한 ES2016(es7)을 사용
"module": "CommonJS", // 반환하는 모듈은 CommonJS 방식
"jsx": "react-jsx", // JSX 컴파일을 React.createElement()가 아닌 _jsx()로 수행
"strict": true, // strict mode family options를 한꺼번에 활성화
"exactOptionalPropertyTypes": true, // optional 속성에 undefined를 할당하려면 optional 대신 직접 명시해야 함
"skipLibCheck": true, // type declaration file에 대한 검사를 수행하지 않음
"esModuleInterop": true, // 모듈 시스템 간의 호환설을 높이기 위한 옵션. 간단히 말해 CommonJS 모듈에서 import문을 사용할 수 있게 한다.
// 코드 작성 시 사용한 절대 경로를 상대 경로로 바꾸기 위한 설정들
"baseUrl": "src",
"paths": {
"types/*": ["types/*"],
"pages/*": ["pages/*"],
"components/*": ["components/*"],
"contexts/*": ["contexts/*"],
"hooks/*": ["hooks/*"],
"utils/*": ["utils/*"],
"assets/*": ["assets/*"],
"constants/*": ["constants/*"]
},
},
"include": ["src/**/*"]
} https://www.typescriptlang.org/tsconfig 여기의 Recommended 설정을 다 같이 훑어보고 필요하겠다 싶은 것들을 켜 주었습니다. 특히 strict가 true면 자동으로 true가 되는 설정은 따로 명시하지 않아 config 파일을 간편하게 만들 수 있었습니다. 🧙♂️ 공유하고 싶은 내용API 통신 계층 분리에 대해저희 팀은 API 통신을 할 때 크게 두 단계를 거치도록 만들었습니다. 1. fetch API를 사용하는 Fetcher, 2. 해당 Fetcher를 사용하는 tanstack query 함수들. 이렇게 만든 이유는 다른 라이브러리들과의 성격을 비교했을 때 저 두 개로 나눌 수 있어서입니다. fetch api는 axios와 서로 바꿀 수 있고, 마찬가지로 tanstack query는 swr등 다른 라이브러리로 교체가 가능해요. 따라서 추후 기술 스택이 변하더라도 쉽게 갈아탈 수 있도록 저런 형태로 만들었습니다. fetcherconst postForm = (form: NewPetPlantRequest) => {
return fetch(PET, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...form }),
});
};
const PetAPI = {
postForm,
};
export default PetAPI; fetcher는 이런 식으로 만들어서 사용합니다. 비슷한 기능에 묶여 있는 로직들을 통합하기 위해서 객체를 사용했습니다. 객체의 네이밍은 airbnb 23.8번을 참고해 function library라고 생각해서 PascalCase로 작성합니다. queryconst useDictSearch = (name: string) =>
useQuery<DictNameSearchResponse, Error, DictNameSearchResult[]>({
queryKey: [DICT, 'search', name],
queryFn: async () => {
const response = await DictAPI.getSearch(name);
const data = await response.json();
return data;
},
});
export default useDictSearch; 만들어진 fetcher는 tanstack query의 query function에서 사용하고 있습니다. 🔗 참고한 자료 |
Beta Was this translation helpful? Give feedback.
-
바톤팀의 생각들어가기 전에
Component선언 방식
Props
Component with Children / Component without Children
Event
// const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => {/*...*/ };
// VS
const handleClick = (event: React.MouseEvent<HTMLButtonElement>): => {/*...*/ };
Hooks기본 훅// const [user, setUser] = useState<User>({});
const [data, setUser] = useState<User | null>(null);
Ref
모듈type 관리 방식
type import/export
APIRequest / Response Type
빌드 설정loader
tsconfig
|
Beta Was this translation helpful? Give feedback.
-
🧐 괜찮을지도 생각들어가기 전에도움이 되었던 부분
불편한 부분
Component선언 방식Props
// bad
type CompProps = {};
// good
interface CompProps {}
// bad
interface Props {...}
// good
interface MayBeFineProps {... Component with Children / Component without Children
interface ComponentProps {
// ...
Children : ReactNode
}
Event
import { ChangeEvent } from React;
// bad
const eventComponent = (event: React.ChangeEventHandler<HTMLInputElement>) => {...}
// good
const eventComponent = (event: ChangeEvent<HTMLInputElement>) => {...} Hooks기본 훅useState
const [name, setName] = useState<string>('');
const [info, setInfo] = useState<InfoType>({
name : 'Kim',
age : 20,
}); useContext
interface MapContextProps {
latitude : number;
longitude : number;
} Ref
const ref = useRef<HTMLInputElement>(null); 모듈type 관리 방식
type import/exportimport type { PinProps } from '../types/Pin';
export interface PinProps {
id: string;
name: string;
address: string;
description: string;
latitude: string;
longitude: string;
updatedAt: string;
} APIRequest / Response Type
// good
interface GetContentRes {
...
}
interface GetContentReq {
...
}
interface PostContentRes {
...
}
interface PutContentRes {
...
} 빌드 설정loader현재 타입체킹이 가능한 ts-loader만을 이용하는 중이지만 이후에 최적화를 진행하면서 babel-loader와 ForkTsCheckerWebpackPlugin을 적용해 빌드 속도 개선 예정 tsconfig"compilerOptions": {
"target": "ES6",
"lib": ["DOM", "DOM.Iterable", "esnext"],//파일에 포함할 DOM 관련 API와 같은 라이브러리를 지정
"allowJs": true,
"skipLibCheck": true,//.d.ts 파일의 타입 검사를 건너뜁
"moduleResolution": "Node",
"strict": true,
"module": "es6",//컴파일에 사용할 모듈 시스템을 지정
"isolatedModules": true, // 각 모듈에서 트랜스파일이 별도로 수행
"allowSyntheticDefaultImports": true, //ES 모듈 형식에서 CommonJS 모듈을 가져올 때, 디폴트(import * as foo from "foo")를 허용
"noEmit": true, // 출력 파일을 생성 x => 타입 체크에만 초점
"jsx": "react-jsx"// TypeScript가 JSX 문법을 처리하는 방법을 명시
},
"exclude": ["node_modules"],
"include": ["./src/**/*.tsx", "./src/**/*.ts"]
} 🧙♂️ 공유하고 싶은 내용
🔗 참고한 자료 |
Beta Was this translation helpful? Give feedback.
-
🧐 스탬프크러쉬팀의 생각들어가기 전에Component선언 방식
Props
interface ComponentsProps {
...
} Component with Children / Component without Children
const Component = ({ ... }:ComponentProps & PropsWithChildren) => {
} Event
const registerCoupon = (event: ChangeEvent<HTMLInputElement>) => { /*...*/ }; Hooks기본 훅
const [user, setUser] = useState<User>({}); Refconst someRef = useRef<HTMLInputElement>(null); 모듈type 관리 방식
interface ComponentProps {}
interface StyledComponentProps {}
type import/export
APIRequest / Response TypeRequest Type
Response Type
빌드 설정loader{
test: /\.(js|ts|tsx)$/i,
exclude: /node_modules/,
use: {
loader: 'ts-loader',
options: {
compilerOptions: { noEmit: false },
},
},
}, tsconfig{
"compilerOptions": {
"target": "es6",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"jsx": "react-jsx",
"module": "esnext",
"moduleResolution": "node",
"allowJs": true,
"checkJs": true,
"outDir": "./dist", // 빌드 파일을 dist로 설정함
"noEmit": false,
"isolatedModules": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
} 🧙♂️ 공유하고 싶은 내용
plugins: [
// ...
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env),
}),
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
publicPath: '/', <-이거!!
},
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
hot: true,
open: true,
}, 🔗 참고한 자료[https://yogjin.tistory.com/entry/webpack-아세요-우아한테크코스-미션-점심-뭐-먹지의-webpack-설정을-보면서-같이-알아봅시다](https://yogjin.tistory.com/entry/webpack-%EC%95%84%EC%84%B8%EC%9A%94-%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-%EB%AF%B8%EC%85%98-%EC%A0%90%EC%8B%AC-%EB%AD%90-%EB%A8%B9%EC%A7%80%EC%9D%98-webpack-%EC%84%A4%EC%A0%95%EC%9D%84-%EB%B3%B4%EB%A9%B4%EC%84%9C-%EA%B0%99%EC%9D%B4-%EC%95%8C%EC%95%84%EB%B4%85%EC%8B%9C%EB%8B%A4) https://www.typescriptlang.org/tsconfig
|
Beta Was this translation helpful? Give feedback.
-
🎧슉 팀의 생각들어가기 전에Component선언 방식컴포넌트는 화살표 함수를 사용한 표현식을 사용한다.
Props
Component with Children / Component without Children
EventEventHandler와 Event의 차이는 느끼지 못했다. Hooks기본 훅Ref초기값을 null로 사용하고 있다. undefined는 정해지지 않은 늬앙스이고, null은 비어있다는 늬앙스를 갖는 타입이라고 생각했다. 그래서 당장 초기값이 비어있다는 의미를 명확하게 하기 위해 null을 사용하기로 결정했다. 모듈type 관리 방식
type import/export타입과 값(컴포넌트, hooks 등)을 구분하기 위해 사용중이다. APIRequest / Response Type아직 정해지지 않았다. 빌드 설정loader빌드 시에 ts-loader 로 타입 체킹을 한 후 babel/preset-typescript을 이용하여 자바스크립트로 컴파일한다. tsconfig{
"compilerOptions": {
//ES6 버전의 JS로 변환합니다.
"target": "ES6",
//컴파일 과정에서 사용하는 라이브러리 목록입니다.
//DOM.Iterable의 경우 DOM요소를 루프를 돌릴 수 있도록 합니다.
"lib": ["DOM", "DOM.Iterable", "ES2023"],
//_jsx()을 사용하여 React 가상 dom 객체를 만들도록 합니다.
"jsx": "react-jsx",
//컴파일을 완료한 파일이 어떤 모듈 시스템으로 적용되는 지 정합니다.
"module": "ESNext",
//컴파일 과정에서 어떤 모듈 시스템으로 해결할 지 정합니다.
"moduleResolution": "Node",
//json도 컴파일해줍니다.
"resolveJsonModule": true,
//js도 컴파일해줍니다.
"allowJs": true,
//모듈을 export 하도록 강제합니다.
"isolatedModules": true,
//각각의 소스파일을 모듈로 만들도록 강제합니다. 타입스크립트에선 import/export가 없으면 전역으로 접근이 가능한데, 해당 속성을 통해 해당 문제를 방지할 수 있습니다.
"esModuleInterop": true,
//파일 이름의 대소문자를 구분합니다.
"forceConsistentCasingInFileNames": true,
//타입스크립트 타입 체크를 엄격하게 합니다.
"strict": true,
//switch fall through를 막습니다.
"noFallthroughCasesInSwitch": true,
//TypeScript가 라이브러리 파일을 체크하지 않도록 설정합니다.
"skipLibCheck": true,
//baseUrl을 root로 지정합니다.
"baseUrl": ".",
//path @/*를 src/*로 읽습니다.
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src", ".storybook/*"]
} 🧙♂️ 공유하고 싶은 내용뿌듯한 부분, 도움이 필요한 부분, 우리 팀의 팁 등등
🔗 참고한 자료 |
Beta Was this translation helpful? Give feedback.
-
목표
템플릿
🧐 우리팀의 생각
들어가기 전에
Component
선언 방식
Props
Component with Children / Component without Children
Event
Hooks
기본 훅
Ref
모듈
type 관리 방식
type import/export
API
Request / Response Type
빌드 설정
loader
tsconfig
🧙♂️ 공유하고 싶은 내용
🔗 참고한 자료
Beta Was this translation helpful? Give feedback.
All reactions