2024 하반기 TIL
30 minutes per day
- this list will be replaced by the toc
7/22: Type vs Interface
회사에서
interface
금지하는 컨벤션이 궁금해졌다
Similarities
- 객체 shape
- index signature 가능
interface IObj {
[key: string]: any;
}
type TObj = {
[key: string]: any,
};
Differences
Declaration Merging (선언 병합)
interface
는 선언 병합 가능type
은 불가능
interface User { name: string; } interface User { age: number; } // Merged into: interface User { name: string; age: number; } type UserType = { name: string, }; type UserType = { age: number, }; // Error: Duplicate identifier 'UserType'.
type
complex 가능 (unions, intersections, mapped types etc)type Status = "success" | "error" | "pending"; type UnionType = User | UserType; type PartialUser = Partial<UserType>;
Inheritance
type
:&
사용 (intersection)- interface:
extends
사용
Primitive Alias (TYPEONLY)
type
은 primitive type alias 가능
type Name = string;
Function Types: (type이 더 concise)
type Add = (a: number, b: number) => number; interface IAdd { (a: number, b: number): number; }
Tuple Types
type
만 가능
type Tuple = [number, string]; interface TupleInterface {} // Not applicable for tuples.
Interface | Type |
---|---|
다른 interface/clases 에 의해 extension, implementation 필요 시 | complex type def |
ideal for API contracts | when felxible, maipulative types are required |
7/23: Named vs Default Export
Treeshaking (tree shaking): dead code 제거
Named Export (
import {a, b, c} from 'abc'
)- 이름이 정해져 있어 consistency 유지에 좋음 (물론 renaming 가능)
- bundler (ex: Webpack) 이 treeshaking 할 때 좋다 (final bundle에 미사용 코드 제거)
Default Export (
import abc from 'abc'
)- import 시 이름 정해야 함 (flexibility)
- airbnb js styled guide: 하나만 export할 때는
default
사용 - tree shaking 시 named export 보다 더 어려움
- What is the benefit of prefer-default-export
- Why and when to use default export over named exports in es6 Modules
7/24: useLayoutEffect
- Render: calculating element styles for DOM Tree
- Paint: 실제 스크린에 layout 표시하고 업데이트
useEffect
: 비동기적으로 실행된다.- component render -> paint ->
useEffect
- => useEffect내부에 DOM 조작이 있을 경우, 화면이 깜빡일 수 있다.
- component render -> paint ->
useLayoutEffect
: 동기적으로 실행된다.- component render ->
useLayoutEffect
-> paint - => 화면 깜빡이지 않는다.
- => 로직 복잡할 경우 사용자가 화면을 보기까지 시간이 오래 걸릴 수 있다.
- component render ->
- 기본적으로 useEffect 사용
- data fetching, event handling, state resetting (UI와 관련 없는 작업)
- 화면에 렌더링이 필요한 경우 useLayoutEffect 사용 괜찮음
- 렌더링 직후 DOM요소를 읽을 때 유용 (scroll position 등)
8/5: React FC 사용 지양하기
⚠️ React 18 이상에서 React.FC는 사라졌다
- cra 기본 템플릿에서 사라짐
implicit
children
React.FC
사용 시children
prop이 optional로 간주됨- prop에 children이 추가되지 않았음에도 불구하고 children 넘기기 가능
- 런타임 에러 발생 X (
FC
미사용 시 잡을 수 있음) - TS뿐만 아니라 거의 모든 프로그래밍 언어에서 안티패턴임
타입스크립트의 제네릭 문법을 지원하지 않는다.
type GenericComponentProps<T> = { prop: T, callback: (t: T) => void, };
const GenericComponent = <T>(props: GenericComponentProps<T>) => {};
- React.FC 사용 시 안됨!!
const GenericComponent: React.FC</* ??? */> = <T>( props: GenericComponentProps<T> ) => {};
네임스페이스 패턴 이용 시 불편함
<Select> <Select.Item /> </Select>
// With FC const Select: React.FC<SelectProps> & { Item: React.FC<ItemProps> } = ( props ) => {}; Select.Item = (props) => {}; // Without FC const Select = (props: SelectProps) => {}; Select.Item = (props: ItemProps) => {};
코드가 길어진다
결론: 그냥 prop을 명시적으로 정의하자
Namespace Pattern이란
- namespace : 구분이 가능하도록 정해놓은 범위나 영역 (이름 공간 선언 -> 다른 이름 공간과 구분)
- 자세한 정보
- JS는 namespacing 기능이 없기 땜누에 JS 의 property 를 이용해 namespace 를 구현 (중첩 객체 가능하기에)
React.FC 사용 지양하기 velog 글 토대로 작성함
8/12: react-native-share 🚀
Troubleshooting 입니다. (unsolved)
produced by ChnoAI based on my “discussion” in ChatGPT
배경
- 현재 개발하고 있는 프로덕트에서는 공유 후 (첫) 성공 시 피드백 팝업을 띄워주어야 함
- BUT, 안드로이드는 공유 성공했음에도 불구하고 공유취소했다는 에러가 뜸
문제상황 (QA) 및 배경
iOS Android 공유 성공 뒤 success 공유 성공 뒤 User did not Share
에러- 이로 인해 공유 성공 처리가 이루어지지 않아 사용자 경험에 부정적인 영향이 있을 수 있음.
과정
- React Native Share Docs에도 관련 내용이 없음
Github Discussion 에서 비슷한 상황 발견했으나 역시나 해결방안이 마련되어 있지 않음
- AS-IS:
Share.open(options) .then((res) => { SuccessCallbackFunction(); }) .catch((err) => { // 공유 취소 시 아무런 동작도 하지 않음 if (err.message === "User did not Share") return; // 실제 실패 시에만 실패했다는 팝업을 띄움 FailCallbackFunction(); });
- 문제점: User did not share 분기처리에 안드로이드 성공이 들어가버림 ;;
해결
- 꽤나 찝찝한 해결법이지만,, 라이브러리를 뜯어볼 시간은 없어서 PM님과 타협함 ㅠ
“공유 취소 또한 성공으로 간주한다”
- 공유를 취소했어도 합성 api를 사용하는 것은 마찬가지기에, 사용자에게 피드백을 물어본다
shareOptions
에failOnCancel: false
추가해주기
const shareOptions = { urls: paths, title: fileName, failOnCancel: false, // <----------- 요 옵션 추가!! }; await Share.open(shareOptions) .then(() => { onSuccess?.(); }) .catch((e) => { onFail?.(String(e)); });
- 꽤나 찝찝한 해결법이지만,, 라이브러리를 뜯어볼 시간은 없어서 PM님과 타협함 ㅠ
후기:
- 찝찝하다. 잘 알고 싶다
- cross-platform 라이브러리더라도 테스트는 꼼꼼히 해봐야 한다
- 앱 개발은 역시 까다롭다
11/4: LCP - Largest Contentful Paint
공부 이유: 최근 webview에서 에디터로 옮긴 컴포넌트로 인해 (default open해두는 팝오버) LCP 극 저하 됨 ㅠ
LCP(Largest Contentful Paint) 최적화하기
- FCP (First Contentful Paint): 초기 DOM 콘텐츠 렌더링하는데 걸리는 시간 측정
LCP: Largest Contentful Paint
- Core Web Vitals 중 하나
- GOOD: 2.5 이내
- BAD: 4.0 이상
LCP 저하 원인
느린 서버 응답 시간
측정법:
TTFB (Time To First Byte)
해결법:
- 서버 최적화, 가까운 CDN 서버 사용, 캐싱 적용, HTML 페이지 우선 캐싱, preconnect 사용
JS와 CSS 렌더링 블로킹
- 블로킹 시간 개선 (CSS 최소화, 중요하지 않은 CSS 지연, 중요 CSS inline)
TBC