총 3부에 걸쳐 크롬 익스텐션의 구조와 제작 방법에 대해 살펴보았다. 마지막으로 Progressive Web Application(PWA) 개념에 대해 살펴보고 브라우저 익스텐션에 대한 포스팅의 대단원(?)의 막을 내리려 한다.
웹 서비스는 서비스를 제공하는 서버에 연결(connect)되어 있을 때에만 서비스를 제공받을 수 있다. 이에 반해 PWA는 서버와 연결되어 있지 않은 상태에서도 최소한의 서비스를 제공받을 수 있도록 만들어진 웹 어플리케이션이다. 여기에 더해 웹 브라우저가 웹 표준을 준수하는 어떠한 문서나 서비스라도 플랫폼(Windows, Linux, MacOS 등)에 관계없이 실행해 주는 것과 마찬가지로 PWA도 웹 브라우저를 가상 실행환경으로 삼기 때문에 하나의 코드로 모든 플랫폼에서 실행되는 프로그램을 만들 수 있다는 장점이 있다.
PWA는 웹 기술(프런트 엔드 기술, 백엔드 기술)을 기반으로 만들어진 어플리케이션이기 때문에 웹 개발을 해 본 경험이 있다면 개념들을 훨씬 수월하게 이해할 수 있고, 또 어플리케이션을 만들 수 있다. PWA의 전체 구조를 아래와 같이 그려볼 수 있다.
PWA에서 Service Worker와 Manifest 요소를 들어내면 아래 그림과 같이 기존 웹 서비스 구조와 같음을 알 수 있다.
전통적인 웹 서비스에서는 자바스크립트 코드에서 Fetch(서버에 데이터 요청) 작업을 수행하고 이를 HTML, CSS와 같은 정적인 요소에 반영한다. 예를 들어 서버로 부터 데이터를 가져와 HTML의 비어있는 부분을 채워 완성하고 사용자에게 보여준다.
PWA에서는 자바스크립트 코드 대신 서비스 워커(Service worker)가 서버에 데이터를 요청하고 응답을 받아 웹 페이지의 자바스크립트에 전달한다. 앞서 PWA가 네트워크가 끊어진 환경 아래에서도 서비스를 제공할 수 있다고 했다. 즉, 마치 사용자의 기기(device)에 설치되어 있는 앱처럼 동작할 수 있는 이유는 서비스 워커가 웹 서버 대신 웹 프런트 엔드의 요청을 처리해주기 때문이다. 서비스 워커의 이와 같은 브릿지(bridge) 또는 대리인(proxy) 역할은 브라우저 익스텐션에 대한 이전 포스팅들에서도 살펴보았기 때문에 브라우저 익스텐션에 대해 알고 있다면 PWA를 좀 더 쉽게 이해할 수 있다. PWA에 대한 자세한 설명은 PWA를 작성하면서 이어가도록 하겠다. 코드를 보면서 PWA에 대해 자세히 살펴보자.
예제로 마이크로소프트에서 제공하는 PWA 예제를 사용하였다(https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/). 하지만 여기 이 포스팅과 마이크로소프트의 블로그 설명 방식은 차이가 있다. 포스팅에서는 먼저 섭씨(℃), 화씨(℉), 절대 온도(K)를 서로 변환해주는 웹 서비스를 만든 후 이를 PWA 형태로 변형시키는 두 단계로 진행하려한다.
먼저 예제 프로젝트를 저장할 폴더를 하나 생성한다. 포스팅에서는 'MySamplePWA'라는 이름으로 폴더를 생성하였다.
웹 서비스를 구성하는 페이지를 작성해보자. 페이지는 index.html, converter.css, 그리고 converter.js 세 파일들로 구성된다. 페이지의 틀을 구성하는 index.html부터 살펴보면 아래와 같다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="stylesheet" href="converter.css">
<link rel="manifest" href="/manifest.json">
<title>Temperature converter</title>
</head>
<body>
<!-- <h1>Temperature converter</h1> -->
<form id="converter">
<label for="input-temp">temperature</label>
<input type="text" id="input-temp" name="input-temp" value="20"/>
<label for="input-unit">from</label>
<select id="input-unit" name="input-unit">
<option value="c" selected>Celsius</option>
<option value="f">Fahrenheit</option>
<option value="k">Kelvin</option>
</select>
<label for="output-unit">to</label>
<select id="output-unit" name="output-unit">
<option value="c">Celsius</option>
<option value="f" selected>Fahrenheit</option>
<option value="k">Kelvin</option>
</select>
<output name="output-temp" id="output-temp" for="input-temp input-unit output-unit">68 F</output>
</form>
<script src="converter.js"></script>
<script>
if ('serviceWorker' in navigator) {
window.navigator.serviceWorker.register('/sw.js', { scope: '/' });
}
</script>
</body>
</html>
<MySamplePWA/index.html>
페이지의 본문인 <body></body> 부분은 크게 데이터를 입력 받는 <form></form> 파트와 온도 변환을 수행하는 <script></script> 두 파트로 구성되어 있다. <form></form> 파트를 살펴보면 <input> - <select> - <select> - <output> 네 요소들로 구성되어 있음을 알 수 있다. <form> 파트에서 가장 먼저 등장하는 <input>은 변환 대상 온도값을 입력받는 요소이고 이어지는 <select> 요소는 입력한 값의 단위(℃, ℉, 또는 K)를 선택할 수 있도록 해준다. <form> 파트의 세 번째 <select> 요소를 통해 변환하기를 원하는 온도 값의 단위가 선택되고 최종적으로 변환 결과가 <output> 요소를 통해 페이지에 표시된다.
페이지의 <body>의 두 번째 파트인 <script> 요소들은 실제 온도 변환을 수행하는 자바스크립트 코드를 지정하는데 사용된다. 여기서 두 번째 <script> 요소는 서비스 워커, 즉 웹 서버의 대리인인 프록시(proxy) 코드를 지정하는데 사용된다.
<script>
if ('serviceWorker' in navigator) {
window.navigator.serviceWorker.register('/sw.js', { scope: '/' });
}
</script>
scope로 지정되는 위치, 즉 프로젝트 폴더에서 sw.js 라는 이름의 자바스크립트 코드를 찾아서 서비스 워커로 등록(register)한다. sw.js 코드는 포스팅의 뒷 부분에서 살펴볼 것이다. 일단은 아래와 같이 sw.js 파일을 빈 내용으로 작성해 놓는다.
// sw.js
<MySamplePWA/sw.js>
온도 단위 변환을 수행하는 converter.js 파일을 아래와 같이 작성하자.
const inputField = document.getElementById('input-temp');
const fromUnitField = document.getElementById('input-unit');
const toUnitField = document.getElementById('output-unit');
const outputField = document.getElementById('output-temp');
const form = document.getElementById('converter');
function convertTemp(value, fromUnit, toUnit) {
if (fromUnit === 'c') {
if (toUnit === 'f') {
return (value*9)/5 + 32;
}
else if (toUnit === 'k') {
return value + 273.15;
}
return value
}
if (fromUnit === 'f') {
if (toUnit === 'c') {
return (value - 32)*5/9;
}
else if (toUnit === 'k') {
return ((value + 459.67)*5)/9;
}
return value
}
if (fromUnit === 'k') {
if (toUnit === 'c') {
return value - 273.15;
}
else if (toUnit === 'f') {
return (value*9)/5 - 459.67;
}
return value
}
throw new Error('Invalid unit');
}
form.addEventListener('input', () => {
const inputTemp = parseInt(inputField.value);
const fromUnit = fromUnitField.value;
const toUnit = toUnitField.value;
const outputTemp = convertTemp(inputTemp, fromUnit, toUnit);
outputField.value = (Math.round(outputTemp*100)/100) + ' ' + toUnit.toUpperCase();
});
<MySamplePWA/converter.js>
converter.js에서는 index.html의 <input>, <select>, <output> 요소들을 찾고 <input>, <select> 요소들의 내용을 이용하여 온도 변환 계산을 수행한다. 온도 변환 계산 코드가 convertTemp(...) 함수에 기술되어 있고 index.html의 온도 값 입력 요소인 <input> 요소에 값이 입력될 때 마다 form.addEventListener에 등록된 이벤트 리스너(화살표 함수로 되어있다.)를 통해 이 converterTemp 함수가 실행된다.
서비스 웹 페이지를 구성하는 마지막 요소는 페이지의 디자인 기술하는 CSS 부분이다. 그 내용을 살펴보면 아래오 같다.
html {
background: rgb(243, 243, 243);
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 15pt;
}
html, body {
height: 100%;
margin: 0;
}
body {
display: grid;
place-items: center;
}
#converter {
width: 15rem;
padding: 2rem;
border-radius: .5rem;
box-shadow: 0 0 2rem 0 #0001;
display: flex;
flex-direction: column;
align-items: center;
}
#converter input, #converter select {
font-family: inherit;
font-size: inherit;
margin-block-end: 1rem;
text-align: center;
width: 10rem;
}
#converter #output-temp {
font-size: 2rem;
font-weight: bold;
}
<MySamplePWA/converter.css>
converter.css 파일 안에는 페이지 각 요소들의 크기(height, width), 표시 방법(display), 간격(margin)과 여백(padding), 그리고 글꼴(font-family, font-size, font-weight) 관련 설정들이 포함되어 있다. 디자인이 사용 경험을 결정하는 중요한 요소이지만 포스팅의 목적인 PWA의 구조와 동작을 살펴보는 것과는 직접적인 관련이 없기 때문에 자세히 설명하지는 않으려한다.
이제 웹 서비스의 백엔드인 서버 코드를 작성할 차례다. 마이크로소프트의 블로그와는 달리 여기 포스팅에서는 파이썬과 플라스크Flask를 이용해 브라우저를 통해 웹 페이지들을 전달받을 수 있도록 구성하였다.
from flask import Flask, send_from_directory
# app = Flask(__name__)
app = Flask(__name__, static_folder='./', static_url_path='/')
@app.route('/')
@app.route('/home')
def home():
response = send_from_directory(app.static_folder, 'index.html')
return response
if __name__ == '__main__':
app.run('0.0.0.0', 8000)
<MySamplePWA/run.py>
run.py 스크립트를 실행하면 웹브라우를 통해 http://127.0.0.1:8000/에 접속해 작성된 서비스를 제공받을 수 있다. run.py 내용에 대한 간단한 설명을 붙이자면 아래와 같다.
# 플라스크 라이브러리에서 Flask, send_from_directory 클래스를 가져온다.
from flask import Flask, send_from_directory
# app = Flask(__name__)
# 실행되는 Flask 앱 이름을 __name__ 키워드로 지정하고(실행 시점에 '__main__'으로 주어질 것이다),
# js, css 파일들(static files)의 위치를 run.py가 위치하는 ./로 지정한다.
# 그리고 URL에서 이들 static file들을 찾을 때 서버 실행 파일과 같은 위치인 /를 참조하도록 path를 설정하였다.
app = Flask(__name__, static_folder='./', static_url_path='/')
# 주소 http://127.0.0.1:8000/ 또는 http://127.0.0.1:8000/home으로 접속하면 index.html 페이지를 받도록 설정하였다.
@app.route('/')
@app.route('/home')
def home():
response = send_from_directory(app.static_folder, 'index.html')
return response
# 이 스크립트가 실행되면 자기 자신(localhost)을 IP로, 포트 번호를 8000번으로 부여받는다.
if __name__ == '__main__':
app.run('0.0.0.0', 8000)
아래와 같이 웹 서비스의 프런트엔드 코드들과 백엔드 코드가 준비되었다.
파이썬 웹 서버 코드인 run.py 파일을 실행하기 위해 파이썬 실행 환경이 필요하다. 컴퓨터에 파이썬을 설치한 후(파이썬 인터프리터를 https://www.python.org/ 에서 받아서 설치) 명령어 프롬프트 또는 에디터(예, VS Code)의 터미널에서 아래 명령들을 차례로 실행하여 실행 환경을 구성한다.
$MySamplePWA> python -m venv ./venv
<파이썬 실행 환경 구성>
$MySamplePWA>.\venv\Scripts\Activate.ps1
또는
$MySamplePWA>.\venv\Scripts\Activate.bat
(venv)$MySamplePWA> pip3 install wheel
(venv)$MySamplePWA> pip3 install Flask
<서버 파이썬 스크립트 실행에 필요한 라이브러리 설치>
파이썬 실행 환경이 구성되고 필요한 라이브러리들이 설치되었으면 아래와 같이 파이썬 언어로 작성한 웹 서버 스크립트 run.py를 실행시킬 수 있다.
(venv)$MySamplePWA> python run.py
이제 웹 브라우저를 열고 서버가 요청을 대기하는 IP 주소인 127.0.0.1:8000 또는 192.168.0.12:8000 주소로 접속하면 웹 브라우저를 통해 서비스를 제공받을 수 있다.
위와 같은 서버-클라이언트 환경에서는 웹 서버를 중지시키면 웹 서비스를 제공받을 수 없다. 명령 프롬프트 또는 에디터의 터미널 창에서 웹 서버를 중지시킨 후(보통 Ctrl + C 키를 누르면 중지된다) 웹 서버의 주소로 다시 접근하면 아래와 같이 서버를 찾을 수 없다는 메시지가 표시된다.
여기까지 작성한 프로그램 내용이 전통적인 웹 서비스 구조에 따른 웹 어플리케이션이다(아래 그림 참조).
이제 이 웹 애플리케이션을 Progressive Web Application(PWA)으로 만들어보자. 글이 길어져서 이 이상 집중해서 읽기는 쉽지 않을 것 같고, 글을 작성하는 입장에서도 포스팅이 계속 늦어지는 것 같아서 PWA 파트는 이어지는 다음 포스팅에서 이어가도록 하겠다.
■
'웹 프로그래밍 고급 주제들' 카테고리의 다른 글
4. 익스텐션 그 너머 - PWA: Progressive Web Application 2/2 (0) | 2024.03.27 |
---|---|
3.2. 크롬 익스텐션 만들어보기 Part II - Chrome API 사용하기 (2) | 2024.03.18 |
3.1. 크롬 익스텐션 만들어보기 Part II - 서비스 워커 심화 (0) | 2024.03.02 |
2.4. 크롬 익스텐션 만들어보기 Part I - 구조 및 예제 정리 (0) | 2024.02.20 |
2.3 크롬 익스텐션 만들어 보기 Part I - 서비스 워커 (0) | 2024.02.08 |
댓글