일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 신촌 소문난집
- graphql with reactnative
- 앙버터마카롱
- 잠실새내 도그존
- graphql react native
- 도그존
- 토라비
- 잠실새내
- 금별맥주
- 홍대 카페 장쌤
- 비동기배열
- 화이트해커를 위한 웹 해킹의 기술
- 예쁜술집 예술
- 홍대 토라비
- promise처리
- 지보싶 신촌점
- 비동기배열처리방법
- useMutation error
- 화이트 해커를 위한 웹 해킹의 기술
- apolloclient
- graphql
- 운정 소바동
- apollo react native
- graphql 400
- promise메서드
- graphql with RN
- graphql mutation error
- 홍대 예술
- typescript
- 고르드
- Today
- Total
yehey's 공부 노트 \n ο(=•ω<=)ρ⌒☆
[Next js/Approuter] google material icon 사용하기 본문
Background
icon을 추가하면서 기존에는 필요한 svg 파일을 다운받아 사용했는데, 이번 프로젝트에서는 정해진 아이콘 없이 이것저것 사용해보고 잘 맞는 아이콘을 적용하고 싶었다. 그래서 google 에서 지원하는 google material icon 을 사용하기로 했다. 현재 프로젝트는 Next.js Approuter 를 사용하고 있다.
Contents
https://fonts.google.com/icons
구글에서 제공하는 개발자 가이드를 확인하면 간단하게 추가할 수 있다. app router 에서는 기본 파일이
app/layout.tsx, page.tsx 가 있는데 이 중에서 layout.tsx 에 적용해주면 된다.
Issue: next/head not working
layout.tsx 에 다음과 같이 next/head 를 추가하고 적용하면 아이콘이 적용되지 않는다.
import Head from 'next/head';
export default function RootLayout({ children }: IRootLayoutProps) {
return (
<html lang="kr">
<Head>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
</Head>
<body>
<div id="portal" />
...
</body>
</html>
);
}
이유는 Next js 문서에 나와있다. https://nextjs.org/docs/messages/no-stylesheets-in-head-component
Next에서 제공하는 Head 컴포넌트에는 stylesheet 속성이 들어갈 수 없다. Suspense / Streaming 과 같이 쓸 경우 깨질 가능성이 존재하기 때문에, 초기 SSR 응답에 포함되는 것이 보장되므로 클라이언트 측 렌더링 시까지 로드가 지연되기 때문에 성능 저하의 문제도 발생할 수 있다.
아무튼 그래서 <Head>
태그 안에 <link rel='stylesheet'>
는 들어갈 수 없다.
My Solution1 : next/head 말고 일반태그 사용하기
export default function RootLayout({ children }: IRootLayoutProps) {
return (
<html lang="kr">
<head>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
</head>
<body>
<div id="portal" />
...
</body>
</html>
);
}
link 가 잘 적용되어있다.
My Solution2: next 의 Metadata 사용하기
https://nextjs.org/docs/app/building-your-application/optimizing/metadata
Metadata를 사용하면 html태그 안에 들어갈 <link>,<meta>태그 등을 정의하는데 사용할 수 있다.
import type { Metadata } from 'next';
export const metadata: Metadata = {
icons: {
other: {
rel: 'stylesheet',
url: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined',
precedence: 'default',
},
},
};
export default function RootLayout({ children }: IRootLayoutProps) {
return (
<html lang="kr">
<body>
<div id="portal" />
...
</body>
</html>
);
}
stylesheet 의 경우 precedence : 'default'
를 주지 않으면 적용이 되지 않는다.
(참조: https://stackoverflow.com/questions/74934561/how-to-add-a-css-file-to-a-pages-head-in-the-app-folder-of-next-js)
javascript 에서는 문제가 되지 않지만, typescript에서는 문제가 된다. Metadata 내부 타입에 precedence는 정의되어있지 않기 때문이다. next 의 Metadata 타입을 살짝 보면 다음과 같다.
//next/dist/lib/metadata/types/metadata-interface.d.ts
interface Metadata extends DeprecatedMetadataFields {
...
icons?: null | IconURL | Array<Icon> | Icons;
...
}
//next/dist/lib/metadata/types/metadata-types.d.ts
export type Icons = {
/** rel="icon" */
icon?: Icon | Icon[];
/** rel="shortcut icon" */
shortcut?: Icon | Icon[];
/**
* @see https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
* rel="apple-touch-icon"
*/
apple?: Icon | Icon[];
/** rel inferred from descriptor, defaults to "icon" */
other?: IconDescriptor | IconDescriptor[];
};
export type IconDescriptor = {
url: string | URL;
type?: string;
sizes?: string;
color?: string;
/** defaults to rel="icon" unless superseded by Icons map */
rel?: string;
media?: string;
/**
* @see https://developer.mozilla.org/docs/Web/API/HTMLImageElement/fetchPriority
*/
fetchPriority?: 'high' | 'low' | 'auto';
};
수정이 필요한 부분은 IconDescriptor 다. 여기에 precedence 속성이 없기 때문에 IconDescriptor를 상속받는 새로운 interface를 생성했다.
// types/custom.ts
import { IconDescriptor } from 'next/dist/lib/metadata/types/metadata-types';
export interface CustomIconDescriptorType extends IconDescriptor {
precedence?: string;
}
// app/layout.tsx
const icon: CustomIconDescriptorType = {
rel: 'stylesheet',
url: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined',
precedence: 'default',
};
export const metadata: Metadata = {
icons: {
other: icon,
},
};
export default function RootLayout({ children }: IRootLayoutProps) {
return (
<html lang="kr">
<body>
<div id="portal" />
...
</body>
</html>
);
}
적용이 잘 된 걸 확인할 수 있다.
google material Icon 컴포넌트화 하기
import styled from 'styled-components';
interface IIconProps {
iconName: string;
size?: string;
}
function Icon({ iconName, size }: IIconProps) {
return (
<Wrapper>
<Span className="material-symbols-outlined" size={size} color={color}>
{iconName}
</Span>
</Wrapper>
);
}
export default Icon;
const Wrapper = styled.div`
display: flex;
`;
type SpanType = Pick<IIconProps, 'size' | 'color'>;
const Span = styled.span<SpanType>`
font-size: ${(props) => props.size ?? '2rem'};
`;
iconName을 span에 넣어주면 google material icon 이 된다.
컬러 수정이 필요한 경우는 폰트 색 수정처럼 color 속성을 Span에 주면 된다.
<Icon iconName='add' size='3rem'/>
사용할 때는 위와 같이 간단히 사용할 수 있다.
Error Review
precedence: 'default' 해결방법을 찾는 건 좀 오래 걸렸는데, 그래도 next 를 사용하는 만큼 next 가 제공하는 Metadata를 이용해서 Icon을 적용하고 싶었다. 그리고 해내서 기분은 좋지만 이게 과연 맞는 방법인지는 잘 모르겠다. 오히려 precedence 속성 없이 html <head> 태그를 사용하는게 더 낫다는 생각도 든다.
참조:
https://nextjs.org/docs/app/building-your-application/optimizing/metadata
https://developers.google.com/fonts/docs/material_symbols?hl=ko
'적어보자 에러 일지' 카테고리의 다른 글
[Next js] input type='date' width 적용 이슈 (1) | 2024.01.14 |
---|---|
[Typescript] 'string' 형식의 식을 인덱스 형식에 사용할 수 없으므로 요소에 암시적으로 'any' 형식이 있습니다. (1) | 2023.12.27 |
[Next js] AppRouter 프로젝트에 styled-component 적용하기 (0) | 2023.12.18 |
[BE/spring boot] JPA DTO 생성 (with. swagger request body) (0) | 2023.12.09 |
[ReactNative, RN, Graphql, Apollo Client] graphql mutation error (0) | 2021.11.19 |