QR 코드 이벤트 등록 앱 - Angular PWA

이 자습서에서는 휴대폰에 설치할 수 있는 간단한 PWA를 만드는 방법을 보여 드리겠습니다. 이 PWA는 QR 코드를 만들고 읽을 수 있습니다. 이 앱을 사용하면 다음 이벤트를 위해 손님을 더 잘 제어할 수 있습니다!





이 앱에서는 이벤트 등록 앱인 것처럼 가정합니다.

최종 코드:
https://github.com/devpato/angular-pwa-qrcode

라이브 앱:
https://qr-app-79289.firebaseapp.com/

각도 앱을 만들어 봅시다!

1) 실행 후 라우팅 생성 여부를 물으면 예를 클릭합니다. 그런 다음 CSS 프로세서를 선택하라는 메시지가 표시되면 SCSS를 선택합니다.

ng new qr-app


2) 휴대폰에 앱을 설치할 수 있도록 앱을 PWA(Progressive Web App)로 만들어 보겠습니다. 프로젝트 내부의 명령줄에서 다음을 실행합니다.

ng add @angular/pwa


3) 등록 구성 요소를 만듭니다.

ng g c registration


4) 스캐너 구성 요소를 만듭니다.

ng g c scanner


5) 게스트 목록 구성 요소를 만듭니다.

ng g c guest-list


6) 게스트 서비스를 생성합니다.

ng g s guest


7) navbar 구성 요소를 만듭니다.

ng g c navbar


8) 프로젝트 내부에 다음 패키지를 설치합니다.

//This is the package that scans QR codes
npm i @zxing/ngx-scanner

//This is the packages that generates QR codes
npm i ngx-qrcode2

//This is the package used to generate a random id for your users
npm i uuid


9) index.html로 이동하고 Semantic UI CDN을 추가하여 앱에 스타일을 지정합니다.

<link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
      integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q="
      crossorigin="anonymous"
    />


10) registration.component.ts 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

import { FormBuilder } from "@angular/forms";
import { Validators } from "@angular/forms";
import { v4 as uuid } from "uuid";
import { GuestService } from "../guest.service";
import { Router } from "@angular/router";
.
.
.
export class RegistrationComponent implements OnInit {
  registrationForm = this.fb.group({
    firstName: ["", Validators.required],
    lastName: ["", Validators.required]
  });

  constructor(
    private fb: FormBuilder,
    private guestService: GuestService,
    private router: Router
  ) {}

  ngOnInit() {}

  onSubmit(): void {
    const guest = { ...this.registrationForm.value, id: uuid() };
    guest.qr = JSON.stringify(guest);
    this.guestService.addGuest(guest);
    this.registrationForm.reset();
    this.router.navigate(["/guests"]);
  }
}


11) registration.component.scss 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

.registration {
  text-align: center;
  margin: 8px;
  &-form {
    form {
      display: flex;
      flex-direction: column;

      .field {
        margin: 8px;
      }
    }
  }
}


12) registration.component.html 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다. 다음 코드는 QR 코드 내부의 정보가 될 새 게스트를 생성합니다.

<div class="registration">
  <h2>QR Code Registration App!</h2>
  <span>Please enter your information to register for the event.</span>
  <div class="registration-form">
    <form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
      <div class="ui left corner labeled input field">
        <input
          type="text"
          placeholder="Fist Name"
          formControlName="firstName"
        />
        <div class="ui left corner label">
          <i class="asterisk icon"></i>
        </div>
      </div>
      <div class="ui left corner labeled input field">
        <input type="text" placeholder="Last Name" formControlName="lastName" />
        <div class="ui left corner label">
          <i class="asterisk icon"></i>
        </div>
      </div>
      <button
        class="ui inverted orange button"
        type="submit"
        [disabled]="!registrationForm.valid"
      >
        Registration
      </button>
    </form>
  </div>
</div>



13) navbar.component.html 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

<div class="ui three item menu">
  <a class="item" [routerLink]="['']">Registration</a>
  <a class="item" [routerLink]="['/scanner']">QR Scanner</a>
  <a class="item" [routerLink]="['/guests']">Guest</a>
</div>


14) guest-list.component.ts 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

import { GuestService } from "../guest.service";
.
.
.
guestList$ = this.guestService.guests$;
  elementType: "url" | "canvas" | "img" = "url";
  constructor(private guestService: GuestService) {}
ngOnInit() {}


15) guest-list.component.html 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

<div class="guest-list">
  <table class="ui celled table" *ngIf="guestList$ | async as guestList">
    <tbody>
      <tr *ngFor="let g of guestList">
        <td data-label="firstName">{{ g.firstName }}</td>
        <td data-label="lastName">{{ g.lastName }}</td>
        <td data-label="qr">
          <ngx-qrcode
            [qrc-element-type]="elementType"
            [qrc-value]="g.qr"
            class="qrcode"
          ></ngx-qrcode>
        </td>
      </tr>
    </tbody>
  </table>
</div>


16) guest-list.component.scss 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

.qrcode {
  display: flex;
  justify-content: center;
}



17) scanner.component.ts 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

import { Guest } from "../guest.model";
import { GuestService } from "../guest.service";
import { map } from "rxjs/operators";
.
.
.
availableDevices: MediaDeviceInfo[];
  currentDevice: MediaDeviceInfo = null;
  hasDevices: boolean;
  hasPermission: boolean;
  qrResult: Guest;
  guestExist: boolean;

  constructor(private guestService: GuestService) {}

  ngOnInit(): void {}

  //Clears the QR code scanned
  clearResult(): void {
    this.qrResult = null;
  }

  //Scans the QR code
  onCodeResult(resultString: string): void {
    this.guestExist = null;
    if (this.checkQRJSON(resultString)) {
      this.qrResult = JSON.parse(resultString);
      this.checkInGuest(this.qrResult);
      this.clearMessage();
    } else {
      this.guestExist = false;
      this.clearMessage();
    }
  }

  //Permission for the app to use the device camera
  onHasPermission(has: boolean): void {
    this.hasPermission = has;
  }

  //Checks if the QR code belongs to a valid guest
  checkInGuest(guestQR: Guest): void {
    this.guestService.guests$
      .pipe(
        map(guests =>
          guests.find((guest: Guest) => guest.id === guestQR.id)
        )
      )
      .subscribe(guest => {
        if (guest !== null && guest !== undefined) {
          this.guestExist = true;
        } else {
          this.guestExist = false;
        }
        this.clearResult();
        this.clearMessage();
      });
  }

  clearMessage() {
    setTimeout(() => {
      this.guestExist = null;
    }, 3000);
  }

  //This function check if the QR code has a valid JSON as data
  checkQRJSON(qrString: string): boolean {
    if (
      /^[\],:{}\s]*$/.test(
        qrString
          .replace(/\\["\\\/bfnrtu]/g, "@")
          .replace(
            /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
            "]"
          )
          .replace(/(?:^|:|,)(?:\s*\[)+/g, "")
      )
    ) {
      return true;
    } else {
      return false;
    }
  }


18) scanner.component.html 파일로 이동한 후 다음 코드를 복사하여 붙여넣습니다.

<div class="qr-scan-area">
  <!-- This is the NPM package in-charge of scanning the QR -->
  <zxing-scanner
    #scanner
    [(device)]="currentDevice"
    (scanSuccess)="onCodeResult($event)"
    (permissionResponse)="onHasPermission($event)"
  ></zxing-scanner>
  <div class="qr-area">
    <div class="area"></div>
  </div>
</div>

<!-- Displays message on the screen if guest is valid or not -->
<div class="guest">
  <ng-container *ngIf="guestExist">
    <div class="ui success message">
      <i class="close icon"></i>
      <div class="header">
        Welcome!!
      </div>
      <p>Guest has been found on the guest lists</p>
    </div>
  </ng-container>
  <ng-container #notFound *ngIf="guestExist === false">
    <div class="ui negative message">
      <i class="close icon"></i>
      <div class="header">
        Warning!
      </div>
      <p>This person is not a guest!</p>
    </div>
  </ng-container>
</div>


19) scanner.component.scss 파일로 이동한 후 다음 코드를 복사/붙여넣기합니다. 이 CSS에서는 스캐너가 더 시원해 보이도록 빨간색 사각형을 그렸지만 스캐너가 작동하는 데 필요하지는 않습니다.

::ng-deep {
  .qr-scan-area {
    position: relative;
    zxing-scanner {
      max-width: 100%;
    }

    .qr-area {
      position: absolute;
      display: flex;
      justify-content: center;
      align-items: center;
      height: calc(100% - 50px);
      top: 0;
      width: 100%;
      .area {
        height: 200px;
        width: 200px;
        border: 2px solid red;
      }
    }
  }
}


20) 앱 폴더 안에 guest.model.ts라는 이름으로 새 인터페이스를 만듭니다.

21) guest.model.ts 내부에 다음을 복사/붙여넣기합니다.

export interface Guest {
  id: string;
  firstName: string;
  lastName: string;
  qr?: string;
}



22) 이제 이전에 생성한 게스트 서비스로 이동합니다. guest.service.ts. 다음 코드는 생성된 모든 새 게스트를 게스트 배열에 추가할 뿐만 아니라 채워진 더미 데이터를 처리하는 코드입니다.

import { Guest } from "./guest.model";
import { BehaviorSubject } from 'rxjs';
.
.
.
private guestSource = new BehaviorSubject<Guest[]>(null);
  guests$ = this.guestSource.asObservable();
  private guests = [
    {
      id: "7558e6e5-3cfa-4c24-b5b7-653ecbd49925",
      firstName: "Pato",
      lastName: "Vargas"
    },
    {
      id: "4847498c-b57f-4ceb-8c0c-8831b9972158",
      firstName: "Diego",
      lastName: "Maradona"
    }
  ];

  constructor() {
    this.populateQR();
    this.guestSource.next(this.guests);
  }

  populateQR(): void {
    this.guests.forEach((g: Guest) => (g.qr = JSON.stringify({ ...g })));
  }

  addGuest(newGuest: Guest): void {
    this.guests.push(newGuest);
    this.guestSource.next(this.guests);
  }


23) app.module.ts로 이동하여 코드에 다음 모듈을 추가합니다.

import { NgxQRCodeModule } from "ngx-qrcode2";
import { ZXingScannerModule } from "@zxing/ngx-scanner";
import { ReactiveFormsModule } from "@angular/forms";
.
.
.
imports: [
    BrowserModule,,
    AppRoutingModule,
    ServiceWorkerModule.register("ngsw-worker.js", {
      enabled: environment.production
    }),
    ReactiveFormsModule,
    ZXingScannerModule,
    NgxQRCodeModule
]


24) 마지막으로, 앱에 대한 경로를 생성해야 합니다. 다음 코드를 복사하여 붙여넣습니다.

import { RegistrationComponent } from "./registration/registration.component";
import { GuestListComponent } from "./guest-list/guest-list.component";
import { ScannerComponent } from "./scanner/scanner.component";

const routes: Routes = [
  {
    path: "",
    component: RegistrationComponent
  },
  {
    path: "guests",
    component: GuestListComponent
  },
  {
    path: "scanner",
    component: ScannerComponent
  }
];


25) app.component.html로 이동하여 거기에 있는 모든 항목을 지우고 다음을 복사/붙여넣기합니다.

<app-navbar></app-navbar>
<router-outlet></router-outlet>


26) 앱을 테스트할 시간입니다. ng serve --o로 앱을 실행합니다. 다음이 표시되어야 합니다.



휴대폰에서 PWA를 사용할 시간입니다.



1) 첫 실행:

ng build --prod


2) 선호하는 호스팅 제공업체에 앱을 배포합니다. 저는 파이어베이스를 좋아합니다. 배포가 매우 쉽습니다. (방법은 따로 알려드리지 않으니 구글링 해주세요.)

3) 앱이 배포되면 휴대폰으로 이동하여 앱이 있는 URL로 이동합니다.

4) 앱이 열리면 PWA이므로 홈 화면에 앱을 추가할 것인지 묻습니다. 아래 그림처럼 보여줍니다.







좋은 웹페이지 즐겨찾기