URL 단축기 만들기 - 12. URL 단축기 UI 만들어보기
이번에는 프론트엔드 코드만 다룰 거에요. 그중에서도 메인페이지네요.
메인 페이지 만들기
대충 간단하게 메인페이지 코드를 만들어 줬어요. 나중에 단축 결과를 표시하는 창도 만들어 줘야해요.
import {
Box,
Button,
Container,
Stack,
TextField,
Typography,
} from '@mui/material'
import React, { FormEvent } from 'react'
export const MainPage: React.FC = () => {
const [url, setUrl] = React.useState('')
const onSubmit = React.useCallback(
(ev: FormEvent<HTMLFormElement>) => {
ev.preventDefault()
alert(url)
},
[url],
)
return (
<Box>
<Container>
<Stack
sx={{ minHeight: '100vh', py: 8 }}
direction="column"
justifyContent="center"
>
<Typography variant="h2" textAlign="center" fontWeight={800}>
URL 단축기
</Typography>
<Stack
sx={{ mt: 2 }}
direction="row"
spacing={2}
component="form"
onSubmit={onSubmit}
>
<TextField
value={url}
onChange={(e) => setUrl(e.target.value)}
type="url"
required
sx={{ flexGrow: 1 }}
label="단축할 URL을 입력해 주세요."
/>
<Button
type="submit"
variant="contained"
disableElevation
sx={{ px: 3 }}
>
단축하기
</Button>
</Stack>
</Stack>
</Container>
</Box>
)
}
이제 API에 요청을 보내볼 거에요. axios를 설치해 볼게요.
import axios from 'axios'
export const api = axios.create()
이제 요청을 보내볼게요.
const { data } = await api.post<UrlCreateResponse>('/urls', { url })
이거만으론 부족하니 UI를 좀 더 쓰고 에러 처리도 한번 해볼게요.
+ 여기서 LoadingButton
컴포넌트 사용을 위해 @mui/lab
패키지를 설치했어요.
import {
Alert,
Box,
Button,
Container,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Stack,
TextField,
Tooltip,
Typography,
} from '@mui/material'
import React, { FormEvent } from 'react'
import { LoadingButton } from '@mui/lab'
import { api } from '../api'
import { UrlCreateResponse } from '../types'
export const MainPage: React.FC = () => {
const [url, setUrl] = React.useState('')
const [processing, setProcessing] = React.useState(false)
const [error, setError] = React.useState<string | null>(null)
const [createdSlug, setCreatedSlug] = React.useState<string | null>(null)
const shortenedUrl = React.useMemo(() => {
if (!createdSlug) return ''
return `${window.location.protocol}//${window.location.host}/${createdSlug}`
}, [createdSlug])
const onSubmit = React.useCallback(
async (ev: FormEvent<HTMLFormElement>) => {
ev.preventDefault()
setProcessing(true)
setError(null)
try {
const { data } = await api.post<UrlCreateResponse>('/urls', { url })
setCreatedSlug(data.slug)
} catch (e) {
setError(`URL 단축 실패: ${e}`)
} finally {
setProcessing(false)
}
},
[url],
)
return (
<Box>
<Dialog open={!!createdSlug} fullWidth maxWidth="xs">
<DialogTitle>URL 단축이 완료되었습니다</DialogTitle>
<DialogContent>
<TextField
fullWidth
variant="standard"
label="단축된 URL"
InputProps={{
readOnly: true,
onClick: (e) => {
;(e.target as HTMLInputElement).select()
},
}}
value={shortenedUrl}
/>
<Button
sx={{ mt: 2 }}
onClick={() => setCreatedSlug(null)}
fullWidth
variant="contained"
disableElevation
>
닫기
</Button>
</DialogContent>
</Dialog>
<Container>
<Stack
sx={{ minHeight: '100vh', py: 8 }}
direction="column"
justifyContent="center"
>
<Typography variant="h2" textAlign="center" fontWeight={800}>
URL 단축기
</Typography>
<Stack
sx={{ mt: 2 }}
direction="row"
spacing={2}
component="form"
onSubmit={onSubmit}
>
<TextField
value={url}
onChange={(e) => setUrl(e.target.value)}
type="url"
required
disabled={processing}
sx={{ flexGrow: 1 }}
label="단축할 URL을 입력해 주세요."
/>
<LoadingButton
type="submit"
variant="contained"
disableElevation
loading={processing}
sx={{ px: 3 }}
>
단축하기
</LoadingButton>
</Stack>
{error && (
<Alert sx={{ mt: 2 }} severity="error">
{error}
</Alert>
)}
</Stack>
</Container>
</Box>
)
}
결과물이에요.


이정도면 UI는 완성이겠죠? 그럼 오늘은 여기까지...
GitHub - paringparing/url-shortener: something
something. Contribute to paringparing/url-shortener development by creating an account on GitHub.