Featured image of post MS Power Apps 로 만든 대여반납기 (1) 시작하기

MS Power Apps 로 만든 대여반납기 (1) 시작하기

feat. Excel / Dataverse

제작동기

사내 동호회에서 물품 대여 반납을 관리하는 시스템을 만드려는 시도가 있었다. 지금까지는 그냥 아주 간단하게, 엑셀 시트에다가 빌려가요~ 반납해요~ 이런 식으로 마구잡이로 쓰고 있었다. 반달같은 사고는 없긴 했지만 소소한 혼란이 있거나 불안한 면이 있었다. 그래서 어떻게 시스템을 만들까? 그냥 간단히 데이터베이스에 데이터 넣고 CRUD 하는 웹 서비스 간단히 만들면 끝나는 일일 수도 있다. 그런데… 서버는 그럼 어디다 두지? 대여하는 사람들의 개인정보가 담긴 회사 내부 정보라 바깥에서 둘 수는 없었다. 그렇다고 사내 서버를 두게 되면 대여반납할 때 마다 VPN 을 연결해야만 한다.

그래서 조금 수고스럽지만, 이미 데이터가 엑셀에 있고, 공식 채널을 Microsoft Teams 에서 쓰고 있으니 여기서 해결할 방법을 찾고 있었다. 그러다가 눈에 띈 것이 Power Apps in Teams 였다. Teams 에서 Power Apps 를 사용하면, VPN 신경쓰지 않아도 되고 MS 에서 관리하는 사내 환경에 데이터를 보관할 수 있으니 괜찮지 않을까? Power Apps 의 로우코드에 낚여 오늘 하루 삽질을 해 보았다.

Power Apps 를 이용한 설계

Power Apps 는 Microsoft 365 의 일부로 제공되는 로우코드 애플리케이션 개발 플랫폼이다. 사용자는 코딩 없이도 애플리케이션을 만들 수 있으며 아니다, 다양한 데이터 소스와 통합할 수 있다. Power Apps 는 비즈니스 프로세스를 자동화하고, 사용자 정의 애플리케이션을 쉽게 만들 수 있도록 도와준다… 라고 한다. 써먹으려면 노코드는 절대 안되고, 객체지향 프로그래밍 개념이나 UI 프로그래밍에 대한 감각은 있어야 한다. 그렇다고 코드가 막 어렵진 않은데… ChatGPT 와 바이브 코딩을 하면서 짜면 된다.

처음에는 엑셀 데이터를 그대로 연결해서, 대여기록과 반납기록을 가져가려고 했지만.. Power Apps 와 엑셀의 궁합이 그렇게 좋지 않다고 한다. 단순히 읽는 것은 무리가 없는데, 엑셀을 데이터베이스처럼 쓰려면 충돌이 잦을 것이라는 경고가 있었다. 실제로 Connector 라는 것으로 연결해 봐도 성능이 썩 나오지 않는 걸 보면 말이다.

그래서 다음과 같이 디자인했다.

  1. 대여기록만 나타내는 Dataverse 테이블을 만든다. (빈번한 쓰기에도 호환이 좋다고 한다.)
  2. 이 Dataverse 테이블을 기반으로 Power Apps 를 만든다.
  3. 물품목록을 나타내는 Excel 테이블을 Power Apps 에 연결한다.
  4. 기타 다양한 검증 코드를 넣는다.

Dataverse는 Microsoft Power Platform의 클라우드 기반 데이터베이스로, Power Apps와 통합되어 데이터를 효율적으로 관리할 수 있다. 관계형 데이터 모델링, 세부적인 보안 설정, 확장성 등으로 데이터 충돌을 최소화할 수 있다고 한다.

그럼 물품 목록도 Dataverse 로 옮겨야 하는 것 아닌가? 라고 생각할 수 있지만, 물품 목록은 자주 바뀌지 않기 때문에 엑셀로 두기로 했다. 엑셀은 Power Apps 에서 읽기 전용으로 연결할 수 있으니, 물품 목록을 수정할 때는 Power Apps 신경쓰지 않고 엑셀을 직접 열어 수정하면 되는 편리함도 있고.

Power Apps in Teams 시작

애당초 목표가 Teams 라는 네트워킹 앱에서 접근할 수 있는 시스템을 만드는 것이었으므로, 여기서는 Teams 에서 Power Apps 를 시작하는 방법을 설명한다.

Power Apps in Teams 첫 화면 (없으면 … 눌러서 앱 추가하자)

Power Apps in Teams 첫 화면 (없으면 … 눌러서 앱 추가하자)

Start 를 누르면 팀을 선택하라고 나오고, 적절한 팀을 선택하면 앱을 만들어 준다. 이름을 지어주고 나면 아래처럼 화면이 나오는데, 쉽게 가기 위해서 With Data 를 선택한다. 우리는 대여기록을 Dataverse 에 저장할 것이므로, 새로운 테이블을 만들기 위해 Create New Table -> Start with a Blank Table 을 선택한다.

휑덩그레한 초기화면…

휑덩그레한 초기화면…

이 다음부터는 노션에서의 테이블 만들기와 비슷하다. 원하는 컬럼과 타입을 만들고 시험삼아 테스트 레코드도 하나 집어넣어 주자. 대여기록에는 뭘 빌릴 것인지, 언제부터 언제까지 빌릴 것인지, 누가 빌리는지 기록하면 된다.

그런데 여기에는 ‘누가’ 를 나타내는 테이블이 없다. 사실은 있다. Owner 라는 테이블 컬럼을 사용할 것이다. 레코드를 입력하거나 수정하는 사람의 MS 계정 정보가 자동으로 기록되기 때문이다.

Dataverse 테이블 만들기

Dataverse 테이블 만들기

여전히 템플릿 화면이 보인다면, 다시 With Data 를 눌러서 아까 만든 테이블을 선택해 주자. 그러면 자기가 알아서 잘~ 만든다.

Dataverse 테이블을 보고 자기가 알아서 만든 편집기 스크린

Dataverse 테이블을 보고 자기가 알아서 만든 편집기 스크린

기본 개념 파악하기

편집기

편집기 구조를 보면,

  • 스크린을 구성하는 객체들의 트리 (왼쪽)
  • 엑셀처럼, 수식을 입력하는 곳 (위)
  • 스크린 (가운데)
  • 객체 속성을 보여주거나 에러를 보여주는 사이드바 (오른쪽)

객체 속성 창에서 수식을 입력하려면 창 크기도 작고 입력할 내용은 많아서 매우 번거롭다. 이 때는 객체 속성의 이름을 클릭하면 수식 입력창으로 이동할 수 있다. 수식 입력창은 크기 조절이 가능하고, 오른쪽 화살표로 펼쳤다 접었다도 가능하기 때문에 편리하다.

스크린

코드가 필요없다고 하지만, 코드를 1도 안 쓴다고는 하지 않았다. 결국 로직을 짜야 하는 부분이 생기기 마련이다. 혹시 예전에 MFC 나 C# 으로 GUI 를 만들어 봤다면 크게 당황할 것이 없을 것이다.

일단 자동으로 만들어 준 스크린을 잘 보면 크게 다음 구조로 되어 있다.

  • 영역을 나누는 컨테이너 (Container)
  • 테이블 레코드를 보여주는 갤러리 (Gallery, 왼쪽 컨테이너)
  • 레코드를 입력하거나 수정할 수 있는 폼 (Form, 오른쪽 컨테이너)
    • 레코드의 각 컬럼을 입력하거나 보여주는 데이터 카드 (DataCard)
      • 문자열, 텍스트박스, 체크박스, 버튼 등을 나타내는 기본 객체 (Object)

객체들은 기본적으로 컨테이너에 배치된다. 또는 데이터 카드 내부에 속할 수 있다. 하지만 폼 바로 밑에는 배치될 수 없다. (폼은 데이터 카드로 이루어진다!)

여기서 가장 중요한 건 갤러리와 폼인데, 갤러리에서 선택한 레코드를 폼으로 옮기는 과정을 이해하지 못하면 ChatGPT 에게 바이브 코딩을 시켜도 계속 도돌이표 질의응답이 이어진다. TBA

  1. 갤러리의 OnSelect 속성을 보면 UpdateContext({ itemSelected: true, CurrentItem: ThisItem }) 라고 되어 있다. 이제부터 갤러리 내부 모든 객체는 ThisItem.<컬럼명> 으로 갤러리에서 선택한 레코드의 값을 가져올 수 있다.
  2. TopBar_RightContainer 에 Edit Button 의 OnSelect 속성을 보자. UpdateContext({selectedRecord: BrowseGallery1.Selected, editMode:true}) 라고 되어 있다. 갤러리에서 선택한 레코드를 selectedRecord 라는 변수에 저장하고, editMode 를 true 로 바꿔준다.
  3. 폼의 DataSource Item 속성은 If(BrowseGallery1.DisplayMode = DisplayMode.Disabled, selectedRecord, BrowseGallery1.Selected) 로 되어 있다. 갤러리에서 선택한 레코드가 selectedRecord 에 저장되어 있다면 그걸 사용하고, 아니라면 갤러리에서 선택한 레코드를 사용한다.

여기서 주목할 부분이 UpdateContext 이다. 이 함수는 현재 스크린에서만 사용할 수 있는 변수를 만들어 준다. 똑같이 ‘선택한’ 레코드라도, 갤러리에서는 ThisItem 으로 접근하고, 폼에서는 selectedRecord 로 접근해야 한다. 문제는 ChatGPT 도 이 부분을 헷갈려 한다는 거다. 정신 바짝 차리기

다음 이 시간엔..

Preview 버튼을 눌러서 실제로 작동하는지 확인할 수 있다. 사실 코드를 건드린 건 하나도 없지만 말이다.

하지만 이렇게만 쓰면 다음과 같은 불상사가 생긴다.

  • 대여일과 반납일을 거꾸로 입력할 수 있고
  • 사용자 A 가 입력한 걸 사용자 B 가 수정/삭제할 수 있다고
  • 물품란에 엉뚱한 것을 적어낼 수 있고

그래서 이런 부분을 막는 코드를 추가해야 한다. 이건 다음 포스트에 따로 정리해 두겠다.

Hugo 기반 / JimmyStack 테마를 사용 중입니다.