서비스
Netuno로 **웹 서비스**를 만들고 시스템 간 데이터 통합을 위한 모든 종류의 복잡한 REST API를 구축할 수 있습니다.
웹 서비스란 무엇인가요?
웹 서비스는 시스템 간에 온라인으로 정보를 표준화된 방식으로 교환할 수 있게 해줍니다. Netuno에서는 server/services/ 안의 각 파일이 자동으로 HTTP를 통해 접근 가능한 엔드포인트가 됩니다.
웹 서비스를 사용하는 이유
비동기 서비스를 사용하면 사용자 경험에 분명한 이점이 있습니다:
- 더 빠른 로딩: 서버가 모든 데이터 처리를 기다리지 않고 페이지의 시각적 구조를 즉시 제공합니다.
- 필요 시 동적 데이터: 프런트엔드는 필요할 때 필요한 데이터만 요청합니다.
- 시스템 간 통합: Netuno에서 생성한 서비스는 모든 외부 애플리케이션(모바일, 다른 웹 시스템 등)에서 사용할 수 있습니다.
- 더 나은 사용자 경험: 사용자는 화면에 무언가가 표시되기 전에 페이지의 전체 처리가 완료될 때까지 기다릴 필요가 없습니다.
사전 요구 사항
시작하기 전에 다음을 확인하세요:
- Netuno가 설치되어 있고 로컬에서 실행 중이어야 합니다.
demo애플리케이션이 애플리케이션 생성에 설명된 대로 생성되어 있어야 합니다.- 작업에 등록된 데이터가 있고 기록과 연결되어 있어야 합니다(반환 값이 비어 있지 않도록 필요).
코드 편집기 설정
Netuno에서 서비스를 생성하고 편집하려면 원하는 코드 편집기를 사용하세요. 무료이고 가볍고 매우 직관적이기 때문에 **Visual Studio Code**를 권장하지만, JavaScript/Python/Groovy와 호환되는 편집기라면 무엇이든 정상적으로 작동합니다.
데모 애플리케이션 열기
- Visual Studio Code를 엽니다.
- 왼쪽 사이드바에서 파일 아이콘을 클릭합니다.
- 폴더 열기(File > Open Folder)를 선택합니다.
- Netuno 설치 폴더로 이동합니다.
- 다음 디렉터리를 선택합니다:
apps/demo - 열기를 클릭합니다.
demo 애플리케이션의 폴더 구조가 왼쪽 패널에 로드됩니다.
서비스 생성
서비스는 서버 측에서 처리됩니다. 새 서비스를 만들려면:
- 파일 패널에서
server/services/폴더를 찾을 때까지 구조를 확장합니다. services폴더를 마우스 오른쪽 버튼으로 클릭합니다.- 새 파일
New File을 선택합니다. - 사용하려는 언어에 따라 파일 이름을 지정합니다:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
server/services/workers.js
server/services/workers.ts
server/services/workers.py
server/services/workers.rb
server/services/workers.kts
server/services/workers.groovy
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
server/services/workers.js
/**
*** 쿼리 데이터를 JSON 출력으로 변환합니다.
*/
const dbWorkers = _db.query(`
SELECT DISTINCT
worker.name, SUM(DATEDIFF(HOUR, work_log.start_time, work_log.end_time)) AS total
FROM worker INNER JOIN work_log
ON worker.id = work_log.worker_id
WHERE worker.active = true AND work_log.active = true
GROUP BY worker.name
ORDER BY total ASC
`);
const items = _val.list();
for (const dbWorker of dbWorkers) {
items.add(
_val.map()
.set("name", dbWorker.getString("name"))
.set("total", dbWorker.getInt("total"))
);
}
_out.json(items);
server/services/workers.ts
/**
*** 쿼리 데이터를 JSON 출력으로 변환합니다.
*/
const dbWorkers = _db.query(`
SELECT DISTINCT
worker.name, SUM(DATEDIFF(HOUR, work_log.start_time, work_log.end_time)) AS total
FROM worker INNER JOIN work_log
ON worker.id = work_log.worker_id
WHERE worker.active = true AND work_log.active = true
GROUP BY worker.name
ORDER BY total ASC
`);
const items = _val.list();
for (const dbWorker of dbWorkers) {
items.add(
_val.map()
.set("name", dbWorker.getString("name"))
.set("total", dbWorker.getInt("total"))
);
}
_out.json(items);
server/services/workers.py
#
# 쿼리 데이터를 JSON 출력으로 변환합니다.
#
dbWorkers = _db.query(
"""SELECT DISTINCT
worker.name, SUM(DATEDIFF(HOUR, work_log.start_time, work_log.end_time)) AS total
FROM worker INNER JOIN work_log
ON worker.id = work_log.worker_id
WHERE worker.active = true AND work_log.active = true
GROUP BY worker.name
ORDER BY total ASC"""
)
items = _val.list()
for dbWorker in dbWorkers:
items.add(
_val.map()
.set("name", dbWorker.getString("name"))
.set("total", dbWorker.getInt("total"))
)
_out.json(items)
server/services/workers.rb
#
# 쿼리 데이터를 JSON 출력으로 변환합니다.
#
dbWorkers = _db.query(%{
SELECT DISTINCT
worker.name, SUM(DATEDIFF(HOUR, work_log.start_time, work_log.end_time)) AS total
FROM worker INNER JOIN work_log
ON worker.id = work_log.worker_id
WHERE worker.active = true AND work_log.active = true
GROUP BY worker.name
ORDER BY total ASC
})
items = _val.list()
dbWorkers.each do |dbWorker|
items.add(
_val.map()
.set("name", dbWorker.getString("name"))
.set("total", dbWorker.getInt("total"))
)
end
_out.json(items)
server/services/workers.kts
/**
*** 쿼리 데이터를 JSON 출력으로 변환합니다.
*/
val dbWorkers = _db.query(
"""SELECT DISTINCT
worker.name, SUM(DATEDIFF(HOUR, work_log.start_time, work_log.end_time)) AS total
FROM worker INNER JOIN work_log
ON worker.id = work_log.worker_id
WHERE worker.active = true AND work_log.active = true
GROUP BY worker.name
ORDER BY total ASC"""
)
val items = _val.list()
dbWorkers.forEach {
items.add(
_val.map()
.set("name", it.getString("name"))
.set("total", it.getInt("total"))
)
}
_out.json(items)
server/services/workers.groovy
/**
*** 쿼리 데이터를 JSON 출력으로 변환합니다.
*/
def dbWorkers = _db.query("""\
SELECT DISTINCT
worker.name, SUM(DATEDIFF(HOUR, work_log.start_time, work_log.end_time)) AS total
FROM worker INNER JOIN work_log
ON worker.id = work_log.worker_id
WHERE worker.active = true AND work_log.active = true
GROUP BY worker.name
ORDER BY total ASC
""")
def items = _val.list()
for (dbWorker in dbWorkers) {
items.add(
_val.map()
.set("name", dbWorker.getString("name"))
.set("total", dbWorker.getInt("total"))
)
}
_out.json(items)
생성한 파일에 아래 코드를 추가하여 서비스를 구현합니다. 이 서비스는 데이터베이스를 조회하고 등록된 총 시간과 함께 작업자 목록을 JSON 형식으로 반환합니다.
코드 이해하기
| 객체/메서드 | 설명 |
|---|---|
_db.query() | SQL 쿼리를 실행하고 키-값(열 → 데이터) 형식의 객체 목록을 반환합니다. |
_val.list() | 결과를 누적할 수 있는 변경 가능한 목록을 만듭니다. |
_val.map() | 각 결과 행에 대한 매핑(키-값) 객체를 만듭니다. |
_out.json() | 객체를 JSON으로 직렬화하고 HTTP 요청에 대한 응답을 전송합니다. |
필요한 데이터
작업에 등록된 데이터가 있고 기록과 연결되어 있는지 확인하세요. 그렇지 않으면 엔드포인트가 빈 목록 []을 반환합니다.
서비스 실행 및 테스트
Netuno가 실행 중인 상태에서 브라우저에서 엔드포인트에 접속합니다:
JSON을 보기 위한 팁
예상 반환 값은 다음과 유사한 JSON입니다:
[
{ "name": "김민수", "total": 12 },
{ "name": "이서연", "total": 28 },
{ "name": "박지훈", "total": 45 }
]
다음 단계
이제 서비스가 작동하므로 다음을 할 수 있습니다:
- 📄 인터페이스 & 디자인 — 이 서비스를 애플리케이션 프런트엔드에 통합합니다.
- 📚 서비스 라이브러리 — Netuno에서 사용할 수 있는
_db,_val,_out및 기타 객체를 살펴봅니다. - 🔗 서비스에 인증 및 액세스 제어를 추가합니다.