안녕하세요, 코더 여러분! 이 짧은 시리즈의 첫 번째 부분에서는 Wails 프레임워크로 만든 비밀번호를 저장하고 암호화하는 데스크톱 애플리케이션의 생성 및 작동을 살펴보았습니다. Go 백엔드와 이를 프론트엔드 측에 바인딩하는 방법에 대해서도 설명했습니다.
이번 부분에서는 사용자 인터페이스를 다루겠습니다. 해당 게시물에서 언급했듯이 Wails를 사용하면 Vanilla JS를 포함하여 우리가 좋아하는 웹 프레임워크를 사용하여 GUI를 구축할 수 있습니다. 내가 말했듯이, Wails의 제작자들은 항상 Svelte를 첫 번째 선택으로 언급하기 때문에 Svelte를 선호하는 것 같습니다. Wails CLI(현재 버전)는 Svelte Typescript(wails init -n myproject -t svelte-ts)를 사용하여 프로젝트 생성을 요청하면 Svelte3을 사용하여 스캐폴딩을 생성합니다. 이미 말했듯이 Svelte5(및 새 기능)를 사용하려는 경우 생성을 자동화하는 bash 스크립트가 있습니다(어쨌든 Wails CLI가 설치되어 있어야 합니다). 게다가 인터페이스 디자인에 딱 맞는 조합인 Taildwindcss Daisyui도 추가되었습니다.
사실 저는 Vanilla Js와 Vue로 먼저 작업한 다음 React로 작업했고, 심지어 많은 사람들이 HTMX(제가 좋아하는 ❤️)입니다. 하지만 Svelte는 시작부터 사랑에 빠지게 만들고, 처음으로 사용한 것은 Wails를 실험하면서였다고 말씀드리고 싶습니다(계속 사용할 것을 약속합니다…). 하지만 웹 프레임워크가 편한 만큼 프론트엔드는 그리 쉽지 않다는 점을 백엔드 개발자에게 상기시켜야 합니다 ?!
본론으로 들어가겠습니다.
웹 프레임워크를 사용해 본 적이 있다면 Wails CLI가 내부적으로 ViteJ를 사용한다는 사실을 금방 알 수 있을 것입니다.
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Vite에서 생성된 웹 프레임워크를 사용해 본 적이 있다면 해당 구성 파일에 놀라지 않을 것입니다. 여기서는 이미 말했듯이 나만의 bash 스크립트를 생성하는 Svelte5(Taildwindcss Daisyui 구성 포함)를 사용합니다. 또한 프론트엔드 개발을 용이하게 하는 TypeScript를 사용하므로 구성도 확인할 수 있습니다.
그런데 이 설명에서 중요한 것은 wailsjs 폴더의 내용입니다. Wails가 수행한 편집이 마법을 발휘한 곳이 바로 여기입니다. go 하위 폴더에는 프론트엔드와 상호작용해야 하는 백엔드 부분의 Js/Ts로 "번역된" 메소드가 저장되는 곳입니다. 예를 들어, main/App.js(또는 해당 TypeScript 버전인 main/App.d.ts)에는 앱 구조의 내보낸(공개) 메서드가 모두 있습니다.
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
모두 약속을 반환합니다. Promise가 반환 유형으로 사용되는 일부 Go 구조를 "래핑"하거나 해당 함수가 인수 유형을 취하는 경우 Go에 해당하는 클래스를 포함하는 모듈(이 경우 TypeScript를 사용하기 때문에 typeds.ts)이 있을 것입니다. 네임스페이스의 구조체와 그 생성자.
또한 런타임 하위 폴더에는 각각 백엔드에서 듣거나 백엔드에서 발생하는 창과 이벤트를 조작할 수 있는 Go 런타임 패키지의 모든 메서드가 포함되어 있습니다.
src 폴더에는 웹 애플리케이션과 마찬가지로 Vite에서 컴파일하여 "frontend/dist"에 저장(최종 실행 파일에 포함)할 파일이 포함되어 있습니다. Tailwindcss를 사용하므로 style.css에는 기본 Tailwind 구성과 사용해야 하는 CSS 클래스가 포함되어 있습니다. 또한 인터페이스에 웹 기술을 적용한 장점으로 하나 이상의 글꼴(폴더 자산/글꼴)을 쉽게 사용하거나 교환할 수 있습니다.
이 개요를 마무리하기 위해 개발 모드(Wails dev)에서 컴파일할 때 핫 리로드를 허용하는 것 외에도 다음에서 변경된 사항(백엔드와 프런트엔드 모두에서)을 관찰할 수 있을 뿐만 아니라 응용 프로그램 창 자체뿐만 아니라 웹 서버가 시작되므로 주소 http://localhost:34115를 통해 웹 브라우저에서도 마찬가지입니다. 이를 통해 선호하는 브라우저 개발 확장을 사용할 수 있습니다. Wails 자신이 우리에게 매우 유용한 개발 도구를 제공한다고 말해야 하지만, 애플리케이션 창(개발 모드에서만)을 마우스 오른쪽 버튼으로 클릭하고 "요소 검사"를 선택하면:
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
보시다시피 Svelte에 4개의 JavaScript 패키지를 추가했습니다(이미 언급한 Tailwindcss Daisyui 제외).
모든 SPA의 진입점은 main.js(또는 main.ts) 파일이므로 다음부터 시작하겠습니다.
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
Wails CLI로 생성된 스켈레톤에 추가한 사항을 강조했습니다. svelte-i18n 라이브러리에서는 fallback/initial 언어 설정과 동시에 번역이 포함된 JSON 파일을 main.js/ts 파일에 등록해야 합니다(비록 우리는 나중에 사용자가 기본 설정으로 선택한 항목에 따라 조작될 것입니다. 번역이 포함된 JSON 파일의 형식은 다음과 같습니다.
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
저는 이 라이브러리의 시스템이 Svelte 애플리케이션 번역을 용이하게 하는 데 쉽고 편리하다고 생각합니다(자세한 내용은 해당 설명서를 참조할 수 있음).
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
JSON 파일을 다른 언어로 번역하는 데 도움이 되는 이와 같은 사이트를 사용할 수도 있습니다. 그러나 문제는 .svelte 파일을 $format으로 채울 때 수동으로 추적해야 한다는 점입니다. 이는 지루하고 오류가 발생하기 쉽습니다. 이 작업을 자동화할 수 있는 방법은 없습니다. 혹시 아시는 분 계시면 알려주시면 감사하겠습니다. 그렇지 않으면 해당 작업을 수행하기 위한 일종의 스크립트를 생각해야 할 것입니다.
모든 Svelte 애플리케이션에서와 마찬가지로 인터페이스 구축의 다음 단계는 App.svelte 파일입니다.
/* main.ts */ import { mount } from 'svelte' import './style.css' import App from './App.svelte' import { addMessages, init } from "svelte-i18n"; // ⇐ ⇐ import en from './locales/en.json'; // ⇐ ⇐ import es from './locales/es.json'; // ⇐ ⇐ addMessages('en', en); // ⇐ ⇐ addMessages('es', es); // ⇐ ⇐ init({ fallbackLocale: 'en', // ⇐ ⇐ initialLocale: 'en', // ⇐ ⇐ }); const app = mount(App, { target: document.getElementById('app')!, }) export default app
여기에서는 애플리케이션을 컴파일할 때 자동으로 생성되고 구조체 앱의 공개 메서드로 선언된 바인딩인 GetMasterPassword를 사용합니다(이 시리즈의 첫 번째 부분 참조). 이 함수는 데이터베이스를 쿼리하고, 마스터 비밀번호가 등록되어 있는 경우 사용자가 이미 등록된 것으로 간주하고(부울 값을 래핑하는 약속을 반환함) 나머지 비밀번호에 액세스할 수 있도록 해당 비밀번호를 입력하도록 요청합니다. 보기의. 데이터베이스에 마스터 비밀번호가 없는 경우 사용자는 "신규"로 간주되며 처음으로 애플리케이션에 진입하기 위해 자신의 비밀번호를 생성하라는 요청을 받습니다.
마지막으로 Login.svelte 구성 요소를 마운트할 때 나머지 애플리케이션에 중요한 작업을 수행합니다. svelte-i18n 라이브러리는 우리가 이미 본 것처럼 초기 언어 코드를 선언하도록 강제하지만 Login.svelte를 마운트할 때 데이터베이스에 (GetLanguage 바인딩을 사용하여) 저장된 언어 코드가 있는지 확인하도록 요청합니다. 데이터베이스가 빈 문자열을 반환하는 경우, 즉 사용자 기본 설정으로 구성된 언어가 없는 경우 svelte-i18n은initialLocale로 구성된 값을 사용합니다. 대신 언어가 구성된 경우 해당 언어가 설정되고(locale.set(result);) "change_titles" 이벤트가 발생하며, 제목 표시줄의 번역된 제목과 앱의 기본 대화 상자가 전달됩니다. 백엔드가 처리할 내용:
/* frontend/src/locales/en.json */ { "language": "Language", "app_title": "Nu-i uita • minimalist password store", "select_directory": "Select the directory where to save the data export", "select_file": "Select the backup file to import", "master_password": "Master Password ?", "generate": "Generate", "insert": "Insert", "login": "Login", ... } /* frontend/src/locales/es.json */ { "language": "Idioma", "app_title": "Nu-i uita • almacén de contraseñas minimalista", "select_directory": "Selecciona el directorio donde guardar los datos exportados", "select_file": "Selecciona el archivo de respaldo que deseas importar", "master_password": "Contraseña Maestra ?", "generate": "Generar", "insert": "Insertar", "login": "Inciar sesión", ... }
로그인 처리 로직은 다음과 같습니다.
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
간단히 말하면, 사용자가 입력하는 내용을 가져오는 입력에 바인딩된 상태인 newPassword는 먼저 onLogin에서 검사하여 6자 이상이고 모두 ASCII 문자인지 확인합니다. 즉, 이 작은 함수 const isAscii = (str: string): boolean => 에 의해 길이가 1바이트에 불과합니다(그 이유는 이 시리즈의 I부에서 참조). /^[x00-x7F] $/.test(str);. 검사에 실패하면 함수가 반환되어 사용자에게 경고 토스트를 표시합니다. 이후 데이터베이스에 저장된 마스터 비밀번호가 없는 경우(isLogin = false) 사용자 유형이 무엇이든 SaveMasterPassword 기능(Wails에서 생성된 바인딩)에 의해 저장됩니다. Promise가 성공적으로 해결되면(데이터베이스에 저장된 레코드의 Id로 uuid 문자열을 반환) 사용자는 svelte-spa-router에 의해 홈 뷰로 이동됩니다. 라이브러리의 푸시 방법. 반대로, 비밀번호가 비ASCII 문자의 길이 및 부재에 대한 검사를 통과하고 DB에 마스터 비밀번호가 있는 경우(isLogin = true) CheckMasterPassword 함수는 저장된 비밀번호에 대해 비밀번호의 신원을 확인하거나 다음 중 하나를 수행합니다. 사용자를 홈 보기로 이동하거나(Promise가 true로 해결됨) 입력한 비밀번호가 올바르지 않음을 나타내는 토스트가 표시됩니다.
애플리케이션의 중앙 뷰이자 동시에 가장 복잡한 뷰는 홈 뷰입니다. 해당 HTML은 실제로 검색 입력이 있는 상단 버튼 표시줄(TopActions 구성 요소), 하단 버튼 표시줄(BottomActions 구성 요소) 및 저장된 비밀번호 항목의 총 수 또는 이들 목록이 다음을 사용하여 표시되는 중앙 영역의 3가지 구성 요소로 세분화됩니다. 스크롤 가능한 창(EntriesList 구성 요소):
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
즉, 검색 상태(searchTerms)를 빈 문자열로 만들어 검색어가 있으면 재설정되어 전체 목록이 표시됩니다. 반면에 상위 구성 요소가 목록을 표시하거나 숨길 수 있도록 showList 상태(TopActions의 props isEntriesList)를 전환합니다.
위 다이어그램에서 볼 수 있듯이 두 하위 구성 요소는 모두 상위의 searchTerms 상태와 동일한 props를 공유합니다. TopActions 구성 요소는 사용자의 입력을 캡처하여 이를 상위 구성 요소 Home에 상태로 전달하고, Home은 이를 하위 구성 요소 EntriesList에 props
로 전달합니다.전체 목록을 표시하거나 사용자가 입력한 검색어로 필터링된 목록을 표시하는 기본 논리는 예상대로 EntriesList 구성 요소에 의해 수행됩니다.
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
앞서 말했듯이 2개의 props(listCounter 및 search)가 수신되고 상태가 유지됩니다(항목 입력: models.PasswordEntry[] = $state([]);). 사용자의 요청에 따라 구성요소가 마운트되면 백엔드에 저장된 비밀번호 항목의 전체 목록을 요청합니다. 검색어가 없으면 해당 상태에 저장됩니다. 있는 경우 획득한 배열에 대한 간단한 필터링이 수행되고 다음 상태에 저장됩니다.
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
표시된 목록에서 사용자는 2가지 작업을 수행할 수 있습니다. 첫 번째는 해당 버튼을 클릭할 때 수행되는 항목의 세부 정보를 표시하는 것입니다. onclick={() => 푸시(`/details/${entry.Id}`)}. 기본적으로 라우팅 라이브러리의 푸시 메소드를 호출하여 사용자를 세부정보 보기로 이동하지만 해당 항목에 해당하는 Id 매개변수를 전달합니다.
사용자가 수행할 수 있는 또 다른 작업은 목록에서 항목을 삭제하는 것입니다. 해당 버튼을 클릭하면 showAlert 기능을 호출하는 확인 팝업이 표시됩니다. 이 함수는 실제로 sweetalert2 라이브러리에 대한 추상화 계층인 showWarning을 호출합니다(sweetalert2 라이브러리를 호출하는 모든 함수는 frontend/src/lib/popups/popups.ts에 있음). 사용자가 삭제 작업을 확인하면 DeleteEntry 바인딩이 호출되고(DB에서 삭제), 반환된 약속이 해결되면 deleteItem이 호출됩니다(항목 상태에 저장된 배열에서 삭제). :
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
홈 보기의 다른 구성 요소(BottomActions)는 훨씬 간단합니다. props를 받지 않으며 사용자를 다양한 보기(설정, 정보 또는 AddPassword)로 리디렉션하는 것으로 제한됩니다.
AddPassword 및 EditPassword 보기는 매우 유사한 논리를 공유하며 로그인 보기와도 유사합니다. 둘 다 사용자가 텍스트 입력에 입력한 내용의 시작과 끝 부분에 공백을 입력하는 것을 허용하지 않으며 암호 길이가 최소 6 ASCII 문자여야 한다는 로그인 보기와 동일한 정책을 따릅니다. 기본적으로 이들을 구별하는 점은 수행해야 하는 작업과 관련된 Wails 생성 링크를 호출한다는 것입니다.
/* main.ts */ import { mount } from 'svelte' import './style.css' import App from './App.svelte' import { addMessages, init } from "svelte-i18n"; // ⇐ ⇐ import en from './locales/en.json'; // ⇐ ⇐ import es from './locales/es.json'; // ⇐ ⇐ addMessages('en', en); // ⇐ ⇐ addMessages('es', es); // ⇐ ⇐ init({ fallbackLocale: 'en', // ⇐ ⇐ initialLocale: 'en', // ⇐ ⇐ }); const app = mount(App, { target: document.getElementById('app')!, }) export default app
다소 복잡한 또 다른 보기는 설정입니다. 여기에는 상위 구성 요소(설정)에서 props 언어 이름을 받는 언어 구성 요소가 있습니다.
/* frontend/src/locales/en.json */ { "language": "Language", "app_title": "Nu-i uita • minimalist password store", "select_directory": "Select the directory where to save the data export", "select_file": "Select the backup file to import", "master_password": "Master Password ?", "generate": "Generate", "insert": "Insert", "login": "Login", ... } /* frontend/src/locales/es.json */ { "language": "Idioma", "app_title": "Nu-i uita • almacén de contraseñas minimalista", "select_directory": "Selecciona el directorio donde guardar los datos exportados", "select_file": "Selecciona el archivo de respaldo que deseas importar", "master_password": "Contraseña Maestra ?", "generate": "Generar", "insert": "Insertar", "login": "Inciar sesión", ... }
이 구성요소의 HTML은 사용자의 언어 선택을 처리하는 단일 select입니다. onchange 이벤트에서는 다음 3가지 작업을 수행하는 함수(handleChange)를 받습니다.
설정 보기로 돌아가면 전체 작업은 백엔드와 주고받는 일련의 이벤트에 의해 관리됩니다. 가장 간단한 것은 종료 버튼입니다. 사용자가 이 버튼을 클릭하면 종료 이벤트가 트리거되어 백엔드에서 수신되고 애플리케이션이 닫힙니다(onclick={() => EventsEmit("quit")}). 팁은 이미 설명한 바와 같이 Esc 키(단축키)가 동일한 작업을 수행함을 사용자에게 알려줍니다.
재설정 버튼은 팝업 창을 표시하는 기능을 호출합니다.
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
사용자가 작업을 수락하면 DB의 모든 컬렉션을 정리하는 Drop 바인딩이 호출되고, 반환된 약속이 해결되면 사용자를 로그인 보기로 보내서 다음을 표시합니다. 작업의 성공을 나타내는 모달입니다.
남은 나머지 두 액션은 서로 비슷하므로 데이터 가져오기를 살펴보겠습니다.
사용자가 해당 버튼을 클릭하면 백엔드에서 수신되는 이벤트가 발생합니다(onclick={() => EventsEmit("import_data")}). 수신되면 사용자가 백업 파일을 선택할 수 있도록 기본 파일 선택 대화 상자가 열립니다. 사용자가 파일을 선택하면 경로(fileLocation)가 포함된 변수에 빈 문자열이 포함되지 않으며 이는 이제 프런트엔드에서 수신되는 백엔드("enter_password")에서 이벤트를 트리거하여 다음을 표시합니다. 내보내기가 수행될 때 사용된 마스터 비밀번호를 묻는 새 팝업 창. 이번에도 프런트엔드는 사용자가 입력한 마스터 비밀번호를 전달하는 또 다른 이벤트("password")를 내보냅니다. 이 새로운 이벤트는 백엔드에서 수신되면 사용자가 선택한 백업 파일에서 DB의 데이터를 읽고 복원하는 작업을 수행하는 Db 패키지의 ImportDump 메서드를 실행합니다. 결과적으로 실행 결과(성공 또는 실패)를 첨부된 데이터로 전달하는 새 이벤트("imported_data")가 생성됩니다. 프런트엔드는 이벤트를 수신하면 다음 두 가지 작업만 수행하면 됩니다.
이 모든 것은 말로 설명하는 것보다 코드 로직으로 보는 것이 훨씬 쉽습니다. ?:
... . ├── index.html ├── package.json ├── package.json.md5 ├── package-lock.json ├── postcss.config.js ├── README.md ├── src │ ├── App.svelte │ ├── assets │ │ ├── fonts │ │ │ ├── nunito-v16-latin-regular.woff2 │ │ │ └── OFL.txt │ │ └── images │ │ └── logo-universal.png │ ├── lib │ │ ├── BackBtn.svelte │ │ ├── BottomActions.svelte │ │ ├── EditActions.svelte │ │ ├── EntriesList.svelte │ │ ├── Language.svelte │ │ ├── popups │ │ │ ├── alert-icons.ts │ │ │ └── popups.ts │ │ ├── ShowPasswordBtn.svelte │ │ └── TopActions.svelte │ ├── locales │ │ ├── en.json │ │ └── es.json │ ├── main.ts │ ├── pages │ │ ├── About.svelte │ │ ├── AddPassword.svelte │ │ ├── Details.svelte │ │ ├── EditPassword.svelte │ │ ├── Home.svelte │ │ ├── Login.svelte │ │ └── Settings.svelte │ ├── style.css │ └── vite-env.d.ts ├── svelte.config.js ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wailsjs ├── go │ ├── main │ │ ├── App.d.ts │ │ └── App.js │ └── models.ts └── runtime ├── package.json ├── runtime.d.ts └── runtime.js ...
프런트엔드(EventsOn)에 리스너를 등록하는 Wails 런타임 함수는 호출 시 해당 리스너를 취소하는 함수를 반환한다는 점을 언급할 가치가 있습니다. 구성 요소가 삭제되면 해당 리스너를 취소하는 것이 편리합니다. React와 마찬가지로 onMount 후크는 리스너가 정리 함수를 반환하도록 하여 해당 리스너를 "정리"할 수 있습니다. 이 경우 별도로 저장하는 예방 조치를 취한 EventsOn에서 반환된 모든 함수를 호출합니다. 변수:
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {models} from '../models'; export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>; export function CheckMasterPassword(arg1:string):Promise<boolean>; export function DeleteEntry(arg1:string):Promise<void>; export function Drop():Promise<void>; export function GetAllEntries():Promise<Array<models.PasswordEntry>>; export function GetEntryById(arg1:string):Promise<models.PasswordEntry>; export function GetLanguage():Promise<string>; export function GetMasterPassword():Promise<boolean>; export function GetPasswordCount():Promise<number>; export function SaveLanguage(arg1:string):Promise<void>; export function SaveMasterPassword(arg1:string):Promise<string>; export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
애플리케이션의 프런트엔드 부분에 대한 검토를 마치려면 About 구성 요소에 대해 언급하는 것만 남았습니다. 이는 about에서 일반적인 것처럼 애플리케이션에 대한 정보를 표시하는 것으로 제한되므로 논리가 거의 없습니다. 그러나 우리가 볼 수 있듯이 보기에는 애플리케이션 저장소에 대한 링크가 표시됩니다. 분명히 일반 웹 페이지에서는 앵커 태그()를 사용하여 해당 링크로 이동하게 되지만 데스크탑 애플리케이션에서는 Wails가 런타임에 이에 대한 특정 기능(BrowserOpenURL)을 갖고 있지 않은 경우에는 이런 일이 발생하지 않습니다. :
/* package.json */ ... }, "dependencies": { "svelte-copy": "^2.0.0", "svelte-i18n": "^4.0.1", "svelte-spa-router": "^4.0.1", "sweetalert2": "^11.14.5" } ...
이렇게 하면 바이너리가 build/bin 폴더에 빌드됩니다. 그러나 다른 빌드 옵션을 선택하거나 크로스 컴파일을 수행하려면 Wails CLI 문서를 살펴보는 것이 좋습니다.
이 애플리케이션에 대해서는 이 시리즈의 첫 번째 부분에서 이미 언급한 것 같습니다. Windows 및 Linux용 컴파일에만 중점을 두었습니다. 이러한 작업(테스트로 인해 반복됨)을 편안하게 수행하기 위해 몇 가지 작은 스크립트와 이를 "조정"하는 Makefile을 만들었습니다.
make create-bundles 명령은 Linux 버전용 애플리케이션과 함께 .tar.xz 압축 파일과 실행 파일을 설치하는 '설치 프로그램' 역할을 하는 Makefile, 시작 메뉴 및 해당 애플리케이션 아이콘. Windows 버전의 경우 바이너리는 dist/라는 폴더 내에서 .zip으로 압축됩니다. 그러나 크로스 플랫폼 자동화 빌드를 선호하는 경우 Wails에는 생성된 아티팩트를 업로드(기본 옵션)할 수 있는 Github Actions가 있습니다. 저장소에 저장하세요.
make create-bundles 명령을 실행할 때 사용하면 Wails 명령 wails build -clean -upx(Linux의 경우) 또는 wails build -skipbindings -s -platform windows/amd64 -를 호출합니다. upx(Windows의 경우). -upx 플래그는 컴퓨터에 설치해야 하는 UPX 유틸리티를 사용하여 바이너리를 압축하는 것을 의미합니다. 실행 파일 크기가 작은 비결 중 하나는 이 유틸리티가 수행하는 탁월한 압축 작업 때문입니다.
마지막으로, 빌드 스크립트는 자동으로 현재 저장소 태그를 정보 보기에 추가하고 빌드 후에 해당 값을 기본값(DEV_VERSION)으로 복원합니다.
휴! 2개의 포스팅이 생각보다 길어졌네요! 하지만 여러분이 이 책을 좋아하셨기를 바라며, 무엇보다도 새로운 프로젝트에 대해 생각하는 데 도움이 되기를 바랍니다. 프로그래밍을 배우다 보면 그런 일이…
이 GitHub 저장소에서 모든 애플리케이션 코드를 찾을 수 있다는 점을 기억하세요.
다른 포스팅에서도 뵙겠습니다. 즐거운 코딩 되셨나요?!
위 내용은 미니멀한 비밀번호 관리자 데스크톱 앱: Golang의 Wails 프레임워크 진출(2부)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!