연말 연초에 약속이 많다는 핑계로 오랜만에 글을 작성합니다.
Outlook Add-in 관한 글을 쓰는 건 더 오랜만인데 이유가 있습니다 🥲
새로운 해를 맞이하면서 회사에 큰 조직 개편이 있었으며, 그 결과 더 이상 outlook 개발을 하지 않게 되었습니다 🥲🥲🥲
대신에 새로운 데스크톱 앱 개발을 시작할 거 같은데, 조만간 새로운 시리즈로 돌아오겠습니다.
---
이번 글은 불친절한 공식문서와 참고할만한 레퍼런스가 없어서 고통받던 '그 시절'에 개발한 소셜 로그인 부분을 작성하려고 합니다.
간략하게 먼저 설명하자면 Web과 Desktop App 두 환경에 동일한 코드를 적용하기 위해서 라이브러리를 사용하지 않았으며,
OAuth 2.0 Redirect URI 방식으로 개발하였습니다.
(환경의 차이점에 대한 자세한 내용은 이전글 를 참고해 주세요) (Outlook Web과 Desktop App 환경 비교하기)
처음에는 Outlook add-in 안에서 Office 제공 함수 (dialog)를 사용해서 소셜 로그인을 구현하는 방법을 설명한 뒤,
커스텀 Hooks으로 분리하는 방법과 분리한 Hooks의 사용 방법을 설명할 예정입니다.
Office Dialog란 ?
반복해서 설명한 것처럼 Web과 Desktop App 모두 적용되는 소설 로그인을 구현하기 위해서는 Microsoft Office에서 제공하는 dialog를 이용해야 합니다.
여기서 dialog란 Microsoft Office 앱의 JS API에서 제공하는 메서드 중 하나이며 Office.context.ui.displayDialogAsync를 이용하여 앱에 적용할 수 있습니다.
dialog는 앱의 주 창과는 별개로 표시되고 사용자와 상호 작용할 수 있는 사용자 인터페이스(인터넷 브라우저)를 제공하며,
이를 통해 추가적인 정보를 입력하거나 사용자가 필요한 작업을 수행할 수 있습니다.
또한, dialog의 크기, IFrame 내부에서 표시할지, 대화 상자가 열리기 전에 사용자에게 경고 메시지를 표시할지 등등
다양한 설정을 할 수 있습니다. (링크 참고)
Dialog 커스텀 Hook으로 분리하기
dialog를 사용하기 위해서는 다음과 같이 4가지를 작성해야 합니다.
- displayDialogAsync를 불러오는 부분
- dialog를 callback 받는 함수
- message를 hanlder 하는 부분
- 이동한 페이지에서 다시 부모 컴포넌트에게 메시지를 전달하는 부분
여기까지만 하더라도 코드가 길어져 가독성이 떨어지는데,
보통 로그인 페이지는 여러 개의 provider를 하나의 페이지에 구현하기 때문에 로직의 분리가 필요한 상황이었습니다.
때문에 프로젝트의 다양한 곳에서 사용할 수 있게 커스텀 훅으로 분리하여 사용했습니다.
useDialog 만들기
우선 공통적으로 사용되는 Office.context.ui.displayDialogAsync 로딩 부분을 openDialog 함수로 정의했습니다.
그 후 dialogCallback으로 dialog에서 일어나는 다양한 에러 상황에 대한 케이스 정리와 성공적으로 로딩이 되었을 때 할 callback를 받는 부분을 구현했습니다.
// @NOTE: dialog 로딩 부분
export const useDialog = () => {
const openDialog = (
url: string,
callback: Function,
dialog: Office.Dialog
) => {
Office.context.ui.displayDialogAsync(
url,
{ width: 50, height: 50 },
(asyncResult: Office.AsyncResult<Office.Dialog>) =>
dialogCallback(callback, asyncResult, dialog)
);
};
// @NOTE: dialogCallback 부분
const dialogCallback = (
callback: Function,
asyncResult: Office.AsyncResult<Office.Dialog>,
dialog: Office.Dialog
) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
switch (asyncResult.error.code) {
case 12004:
console.log('Domain is not trusted');
break;
case 12005:
console.log('HTTPS is required');
break;
case 12007:
console.log('A dialog is already opened.');
break;
default:
console.log(asyncResult.error.message);
break;
}
} else {
dialog = asyncResult.value;
dialog.addEventHandler(
Office.EventType.DialogMessageReceived,
(arg: Office.DialogParentMessageReceivedEventArgs) =>
callback(arg, dialog)
);
}
};
return openDialog;
};
export default useDialog;
useDialog 프로젝트 적용 및, MessageHandler 작성하기
그 후 dialog를 사용할 컴포넌트에서 useDialog()를 불러와줍니다.
openDialog에선 이동을 원하는 URL과 MessageHandler와 아까 선언한 dialog를 파라미터로 받으면 됩니다.
여기서 dialog 변수를 useDialog 훅에 포함하지 않고 사용할 컴포넌트에서 선언한 이유는 한 페이지에서 여러 dialog를 사용할 경우 dialog 중복 선언되어 최적화에 문제가 있었고, 이벤트 간섭을 최소한 하기 위함입니다.
여기서 MessageHandler는 성공적으로 dialog에서 응답을 받을 경우 할 행동을 정의하면 되는데, 중요한 점은 dialog.close() 함수를 마지막에 정의하여 dialog 이벤트를 종료시켜야 합니다.
dialogCallback
// @NOTE: 이렇게 불러오고
let dialog;
const openDialog = useDialog();
~~
// @NOTE: 이벤트 핸들러 정의
const MessageHandler = (arg, dialog) => {
~~
dialog.close();
};
~~
// @NOTE: 이렇게 사용!
openDialog(
"원하는 URL",
MessageHandler,
dialog
)
이동한 페이지에서 부모 컴포넌트에게 메시지를 전달하는 부분 작성하기
이동한 페이지에선 원하는 로직을 작성하면 됩니다.
필자의 경우 Redirect URI 방식으로 각 소셜 로그인을 구현하고,
각 provider에게 전달받은 refresh token이나 id_token 같은 데이터 등을 전달하였습니다.
이때 주의할 점은 이동한 페이지 안에서 office 제공 함수를 사용할 수 있어야 하며,
Office.context.ui.messageParent 메서드를 이용하여 부모 컴포넌트에게 메시지를 전달해야 합니다.
// @NOTR: office 관련 함수(환경) 설정
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
```
```js
// @NOTE: 전달할 내용을 text에 담아 전달
const handleOfficeContext = (text) => Office.initialize = () => Office.context.ui.messageParent(text);
끝!
'WEB > Outlook Add-in' 카테고리의 다른 글
[Outlook add-in] 데스크탑에서 디버깅하기 (Office Add in 개발은 Mac을 강력 추천하는 이유) (0) | 2024.02.25 |
---|---|
[Outlook add-in] Office 함수 활용 및 Web과 Desktop App의 차이점 (2) | 2023.10.16 |
[Outlook add-in] 프로젝트 생성 및 세팅 - 1 (0) | 2023.07.24 |
[Outlook add-in] 프로젝트를 시작하며 - 0 (0) | 2023.07.18 |