>백엔드 개발 >Golang >미니멀한 비밀번호 관리자 데스크톱 앱: Golang의 Wails 프레임워크 진출(2부)

미니멀한 비밀번호 관리자 데스크톱 앱: Golang의 Wails 프레임워크 진출(2부)

Susan Sarandon
Susan Sarandon원래의
2024-12-30 09:50:16517검색

안녕하세요, 코더 여러분! 이 짧은 시리즈의 첫 번째 부분에서는 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 JsVue로 먼저 작업한 다음 React로 작업했고, 심지어 많은 사람들이 HTMX(제가 좋아하는 ❤️)입니다. 하지만 Svelte는 시작부터 사랑에 빠지게 만들고, 처음으로 사용한 것은 Wails를 실험하면서였다고 말씀드리고 싶습니다(계속 사용할 것을 약속합니다…). 하지만 웹 프레임워크가 편한 만큼 프론트엔드는 그리 쉽지 않다는 점을 백엔드 개발자에게 상기시켜야 합니다 ?!

본론으로 들어가겠습니다.

I - 프론트엔드 구조 살펴보기

웹 프레임워크를 사용해 본 적이 있다면 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 자신이 우리에게 매우 유용한 개발 도구를 제공한다고 말해야 하지만, 애플리케이션 창(개발 모드에서만)을 마우스 오른쪽 버튼으로 클릭하고 "요소 검사"를 선택하면:

A minimalist password manager desktop app: a foray into Golang

II - 이제… HTML, CSS, JavaScript에 대해 알아볼까요?


// 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 제외).

  • svelte-copy, 사용자 이름과 비밀번호를 클립보드에 더 쉽게 복사할 수 있습니다.
  • svelte-i18n, i18n 처리용, 즉 사용자가 애플리케이션의 언어를 변경할 수 있도록 허용합니다.
  • svelte-spa-router는 Svelte용 소형 라우팅 라이브러리로, 애플리케이션 창에서 보기를 더 쉽게 변경할 수 있습니다. 이 경우에는 다음에서 제공하는 "공식" 라우팅을 사용할 가치가 없기 때문입니다. SvelteKit.
  • sweetalert2, 기본적으로 모달/대화 상자를 쉽고 빠르게 만드는 데 사용하세요.

모든 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가 성공적으로 해결되면(데이터베이스에 저장된 레코드의 Iduuid 문자열을 반환) 사용자는 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은 이를 하위 구성 요소 EntriesListprops

로 전달합니다.

전체 목록을 표시하거나 사용자가 입력한 검색어로 필터링된 목록을 표시하는 기본 논리는 예상대로 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)를 받습니다.

  • svelte-i18n 라이브러리를 사용하여 프런트엔드에서 언어를 설정합니다
  • Wails 런타임이 애플리케이션 제목 표시줄의 제목과 관련하여 디렉토리 선택파일 선택 대화 상자의 제목을 변경하도록 이벤트("change_titles")를 생성합니다. 이전 작업으로
  • 사용자가 선택한 언어를 DB에 저장하여 다음에 애플리케이션을 시작할 때 해당 언어로 구성된 앱이 열리도록 합니다.

설정 보기로 돌아가면 전체 작업은 백엔드와 주고받는 일련의 이벤트에 의해 관리됩니다. 가장 간단한 것은 종료 버튼입니다. 사용자가 이 버튼을 클릭하면 종료 이벤트가 트리거되어 백엔드에서 수신되고 애플리케이션이 닫힙니다(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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.