암호 키 생성 페이지 레이아웃 구현

This commit is contained in:
static
2024-12-26 21:21:20 +09:00
parent da4b753c41
commit 552681115a
8 changed files with 249 additions and 3 deletions

View File

@@ -18,6 +18,7 @@
},
"devDependencies": {
"@eslint/compat": "^1.2.3",
"@iconify-json/material-symbols": "^1.2.12",
"@sveltejs/adapter-node": "^5.2.9",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
@@ -38,6 +39,7 @@
"tailwindcss": "^3.4.9",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"unplugin-icons": "^0.22.0",
"vite": "^5.4.11"
},
"dependencies": {

145
pnpm-lock.yaml generated
View File

@@ -28,6 +28,9 @@ devDependencies:
'@eslint/compat':
specifier: ^1.2.3
version: 1.2.4(eslint@9.17.0)
'@iconify-json/material-symbols':
specifier: ^1.2.12
version: 1.2.12
'@sveltejs/adapter-node':
specifier: ^5.2.9
version: 5.2.11(@sveltejs/kit@2.15.0)
@@ -88,6 +91,9 @@ devDependencies:
typescript-eslint:
specifier: ^8.0.0
version: 8.18.2(eslint@9.17.0)(typescript@5.7.2)
unplugin-icons:
specifier: ^0.22.0
version: 0.22.0(svelte@5.16.0)
vite:
specifier: ^5.4.11
version: 5.4.11
@@ -107,6 +113,24 @@ packages:
'@jridgewell/trace-mapping': 0.3.25
dev: true
/@antfu/install-pkg@0.4.1:
resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==}
dependencies:
package-manager-detector: 0.2.8
tinyexec: 0.3.1
dev: true
/@antfu/install-pkg@0.5.0:
resolution: {integrity: sha512-dKnk2xlAyC7rvTkpkHmu+Qy/2Zc3Vm/l8PtNyIOGDBtXPY3kThfU4ORNEp3V7SXw5XSOb+tOJaUYpfquPzL/Tg==}
dependencies:
package-manager-detector: 0.2.8
tinyexec: 0.3.1
dev: true
/@antfu/utils@0.7.10:
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
dev: true
/@esbuild-kit/core-utils@3.3.2:
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
deprecated: 'Merged into tsx: https://tsx.is'
@@ -842,6 +866,31 @@ packages:
engines: {node: '>=18.18'}
dev: true
/@iconify-json/material-symbols@1.2.12:
resolution: {integrity: sha512-2p2T13Kccy7R2HNbdiVsIcHxjp4s9a+iKlfbtt29hldG1pVNaPIlMALNA9bjdEwPjwsVFe06INCbjCRc68JysQ==}
dependencies:
'@iconify/types': 2.0.0
dev: true
/@iconify/types@2.0.0:
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
dev: true
/@iconify/utils@2.2.1:
resolution: {integrity: sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==}
dependencies:
'@antfu/install-pkg': 0.4.1
'@antfu/utils': 0.7.10
'@iconify/types': 2.0.0
debug: 4.4.0
globals: 15.14.0
kolorist: 1.8.0
local-pkg: 0.5.1
mlly: 1.7.3
transitivePeerDependencies:
- supports-color
dev: true
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@@ -1630,6 +1679,10 @@ packages:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
/confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
dev: true
/cookie@0.6.0:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
@@ -2470,6 +2523,10 @@ packages:
resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==}
dev: true
/kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
dev: true
/levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@@ -2492,6 +2549,14 @@ packages:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: true
/local-pkg@0.5.1:
resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
engines: {node: '>=14'}
dependencies:
mlly: 1.7.3
pkg-types: 1.2.1
dev: true
/locate-character@3.0.0:
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
dev: true
@@ -2589,6 +2654,15 @@ packages:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
dev: false
/mlly@1.7.3:
resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==}
dependencies:
acorn: 8.14.0
pathe: 1.1.2
pkg-types: 1.2.1
ufo: 1.5.4
dev: true
/mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@@ -2701,6 +2775,10 @@ packages:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
dev: true
/package-manager-detector@0.2.8:
resolution: {integrity: sha512-ts9KSdroZisdvKMWVAVCXiKqnqNfXz4+IbrBG8/BWx/TR5le+jfenvoBuIZ6UWM9nz47W7AbD9qYfAwfWMIwzA==}
dev: true
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -2730,6 +2808,10 @@ packages:
minipass: 7.1.2
dev: true
/pathe@1.1.2:
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
dev: true
/picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
dev: true
@@ -2754,6 +2836,14 @@ packages:
engines: {node: '>= 6'}
dev: true
/pkg-types@1.2.1:
resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
dependencies:
confbox: 0.1.8
mlly: 1.7.3
pathe: 1.1.2
dev: true
/postcss-import@15.1.0(postcss@8.4.49):
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'}
@@ -3346,6 +3436,10 @@ packages:
globrex: 0.1.2
dev: true
/tinyexec@0.3.1:
resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
dev: true
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -3406,9 +3500,56 @@ packages:
hasBin: true
dev: true
/ufo@1.5.4:
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
dev: true
/undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
/unplugin-icons@0.22.0(svelte@5.16.0):
resolution: {integrity: sha512-CP+iZq5U7doOifer5bcM0jQ9t3Is7EGybIYt3myVxceI8Zuk8EZEpe1NPtJvh7iqMs1VdbK0L41t9+um9VuuLw==}
peerDependencies:
'@svgr/core': '>=7.0.0'
'@svgx/core': ^1.0.1
'@vue/compiler-sfc': ^3.0.2 || ^2.7.0
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0
vue-template-compiler: ^2.6.12
vue-template-es2015-compiler: ^1.9.0
peerDependenciesMeta:
'@svgr/core':
optional: true
'@svgx/core':
optional: true
'@vue/compiler-sfc':
optional: true
svelte:
optional: true
vue-template-compiler:
optional: true
vue-template-es2015-compiler:
optional: true
dependencies:
'@antfu/install-pkg': 0.5.0
'@antfu/utils': 0.7.10
'@iconify/utils': 2.2.1
debug: 4.4.0
kolorist: 1.8.0
local-pkg: 0.5.1
svelte: 5.16.0
unplugin: 2.1.0
transitivePeerDependencies:
- supports-color
dev: true
/unplugin@2.1.0:
resolution: {integrity: sha512-us4j03/499KhbGP8BU7Hrzrgseo+KdfJYWcbcajCOqsAyb8Gk0Yn2kiUIcZISYCb1JFaZfIuG3b42HmguVOKCQ==}
engines: {node: '>=18.12.0'}
dependencies:
acorn: 8.14.0
webpack-virtual-modules: 0.6.2
dev: true
/update-browserslist-db@1.1.1(browserslist@4.24.3):
resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
hasBin: true
@@ -3478,6 +3619,10 @@ packages:
vite: 5.4.11
dev: true
/webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
dev: true
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}

3
src/app.d.ts vendored
View File

@@ -1,5 +1,8 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
import "unplugin-icons/types/svelte";
declare global {
namespace App {
// interface Error {}

View File

@@ -0,0 +1,57 @@
<script lang="ts">
import { Button, TextButton } from "$lib/components/buttons";
import Order from "./Order.svelte";
import IconKey from "~icons/material-symbols/key";
const orders = [
{
title: "암호 키는 공개 키와 개인 키로 구성돼요.",
description: "공개 키로 암호화된 데이터는 개인 키로만 복호화할 수 있어요.",
},
{
title: "공개 키는 서버에 저장돼요.",
description: "대신, 개인 키는 이 디바이스에만 저장돼요.",
},
{
title: "다른 디바이스에서 공개 키를 이용해 데이터를 암호화하면,",
},
{
title: "이 디바이스에서만 안전하게 복호화할 수 있어요.",
description:
"서버를 포함한 제3자는 데이터의 내용을 알 수 없어요. 개인 키가 이 디바이스에만 저장되기 때문이에요.",
},
];
</script>
<svetle:head>
<title>암호 키 생성하기</title>
</svetle:head>
<div class="flex h-full flex-col justify-between">
<div class="mt-[20%]">
<div class="flex flex-col gap-y-2">
<h1 class="text-3xl font-bold">암호 키 생성하기</h1>
<p>회원님의 디바이스 간의 안전한 데이터 동기화를 위해 암호 키를 생성해야 해요.</p>
</div>
<div class="my-4 flex flex-col gap-y-2">
<div class="mb-4">
<IconKey class="mx-auto text-7xl" />
<p class="text-center text-xl font-bold text-primary-500">왜 암호 키가 필요한가요?</p>
</div>
<div>
{#each orders as { title, description }, i}
<Order order={i + 1} isLast={i === orders.length - 1} {title} {description} />
{/each}
</div>
</div>
</div>
<div class="sticky bottom-0 flex w-full flex-col items-center gap-y-2 bg-white">
<div class="w-full">
<Button>새 암호 키 생성하기</Button>
</div>
<div class="w-fit">
<TextButton>키를 갖고 있어요</TextButton>
</div>
</div>
</div>

View File

@@ -0,0 +1,33 @@
<script lang="ts">
interface Props {
order: number;
isLast?: boolean;
title: string;
description?: string;
}
let { order, isLast = false, title, description }: Props = $props();
</script>
<div class="items-strech flex gap-x-3 {isLast ? 'mb-0' : 'mb-2'}">
<div class="flex flex-col">
<p
class="flex h-8 w-8 items-center justify-center rounded-full bg-gray-200 text-lg font-bold text-gray-700"
>
{order}
</p>
{#if !isLast}
<div class="mx-auto mt-2 w-0.5 flex-grow rounded-full bg-gray-300"></div>
{/if}
</div>
<div>
<p class="flex min-h-8 items-center text-lg font-medium">
{title}
</p>
<p class="mb-5 mt-1 text-gray-600">
{#if description}
{description}
{/if}
</p>
</div>
</div>

View File

@@ -25,12 +25,12 @@
<h1 class="text-3xl font-bold">환영합니다!</h1>
<p>서비스를 이용하려면 로그인을 해야해요.</p>
</div>
<div class="mt-4 flex flex-col gap-y-2">
<div class="my-4 flex flex-col gap-y-2">
<TextInput bind:value={email} placeholder="이메일" />
<TextInput bind:value={password} placeholder="비밀번호" type="password" />
</div>
</div>
<div class="sticky bottom-0 flex w-full flex-col items-center gap-y-2">
<div class="sticky bottom-0 flex w-full flex-col items-center gap-y-2 bg-white">
<div class="w-full">
<Button onclick={login}>로그인</Button>
</div>

View File

@@ -1,6 +1,12 @@
import { sveltekit } from "@sveltejs/kit/vite";
import Icons from "unplugin-icons/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [sveltekit()],
plugins: [
sveltekit(),
Icons({
compiler: "svelte",
}),
],
});