시작하기
기본 개념
파일 전송을 작업 공간별로 분리해 관리하기
파일 작업 공간은 특정 업무에서 사용하는 파일, 저장 위치, 전송 대상, 접근 범위를 하나의 공간으로 구성하는 방식입니다.
예를 들어 개발팀, 콘텐츠팀, 데이터 분석팀이 각각 사용하는 파일을 별도의 작업 공간으로 구성하고, 각 공간마다 필요한 저장 위치와 전송 장비를 연결할 수 있습니다.
File Workspace
│
┌────┼───────────────┐
▼ ▼ ▼
Files Storage Access
│ │ │
▼ ▼ ▼
Transfer Targets Users / Devices

이렇게 구성하면 여러 업무에서 사용하는 파일과 전송 환경을 작업 공간별로 구분하고 관리할 수 있습니다.
공간 흐름
파일 저장부터 전송과 결과 확인까지 이어가기
사용자는 접근 권한이 설정된 작업 공간을 선택하고 필요한 파일을 저장하거나 확인합니다.
작업 공간에 연결된 전송 작업을 실행하면 선택한 파일이 지정된 장비와 업무 환경으로 전송되며, 실행 결과도 같은 공간에서 확인할 수 있습니다.
작업 공간 선택
│
▼
파일 저장 · 확인
│
▼
전송 작업 실행
│
▼
대상 장비 반영
│
▼
처리 결과 확인

분리 효과
여러 업무와 장비의 파일을 목적에 맞게 관리하기
업무별로 작업 공간을 구성하면 파일과 저장 위치, 전송 대상, 접근 범위를 하나의 관리 기준으로 정리할 수 있습니다.
| 구분 | 개별 관리 | 작업 공간 관리 |
|---|---|---|
| 파일 | 여러 위치의 파일을 각각 확인 | 업무별 파일을 공간별로 관리 |
| 저장 위치 | 파일마다 위치 확인 | 작업 공간별 저장 위치 구성 |
| 전송 대상 | 작업마다 대상 확인 | 공간별 전송 장비와 경로 연결 |
| 접근 범위 | 사용자별 파일 확인 | 작업 공간 기준으로 접근 범위 관리 |
| 결과 확인 | 작업별 결과 확인 | 공간별 실행 결과 확인 |
업무팀
필요한 파일을 작업 공간에서 관리하고 활용하기
공간 선택
업무에 맞는 파일과 작업을 한곳에서 확인하기
업무를 시작할 때 필요한 파일과 작업이 구성된 작업 공간을 선택합니다.
작업 공간에는 해당 업무에서 사용하는 파일과 연결된 전송 작업이 함께 구성되어 있어, 담당자는 현재 업무에 필요한 공간에서 파일 작업을 진행할 수 있습니다.
My Workspaces
│
├── Project A
│ ├── Files
│ └── Transfers
│
├── Media Team
│ ├── Files
│ └── Transfers
│
└── Data Analysis
├── Files
└── Transfers

파일 활용
필요한 파일을 저장하고 다음 업무에 활용하기
작업 공간에서 업무에 필요한 파일을 저장하고, 현재 작업에 사용할 파일을 확인합니다.
저장된 파일은 해당 작업 공간에 연결된 업무 흐름과 함께 활용할 수 있어, 담당자는 필요한 파일을 선택해 다음 작업을 이어갈 수 있습니다.
Workspace
│
├── Upload Files
│
├── Browse Files
│
└── Select Files
│
▼
Next Work
전송 실행
준비된 전송 작업으로 파일 보내기
필요한 파일을 선택한 후 작업 공간에 구성된 전송 작업을 실행합니다.
담당자는 현재 업무에서 사용할 파일과 실행할 작업을 선택하고, 진행 상태와 처리 결과를 확인할 수 있습니다.
Select Files
│
▼
Select Transfer
│
▼
Run
│
┌────┴────┐
▼ ▼
Progress Target
│ │
└────┬────┘
▼
Result

업무팀은 작업 공간에서 필요한 파일을 관리하고 준비된 전송 흐름을 활용해 다음 업무까지 이어갈 수 있습니다.
IT 엔지니어
파일 작업 공간과 전송 환경을 구성하고 중앙에서 관리하기
공간 구성
업무별 파일 저장 위치와 관리 범위 구성하기
업무 목적에 따라 작업 공간을 생성하고, 각 공간에서 사용할 파일 저장 위치와 관리 범위를 구성합니다.
예를 들어 개발 결과물, 미디어 파일, 데이터 처리 결과를 각각 별도의 작업 공간으로 구성할 수 있습니다.
Organization
│
┌────┼───────────────┐
▼ ▼ ▼
Dev Media Data
│ │ │
▼ ▼ ▼
Storage Storage Storage
전송 연결
작업 공간과 대상 장비, 파일 경로 연결하기
각 작업 공간에 사용할 전송 대상 장비와 파일 경로를 연결합니다.
서버, 스토리지, 업무 장비를 작업 공간에 연결하고 파일 전송 경로를 설정해 업무별 전송 환경을 구성할 수 있습니다.
Workspace
│
├── Source Storage
│
├── Transfer Flow
│
└── Target Devices
│
┌──────┼──────┐
▼ ▼ ▼
Server Storage System

접근 정책
사용자와 장비별 접근 범위와 전송 기준 관리하기
작업 공간마다 사용자와 그룹의 접근 범위, 연결 가능한 장비, 파일 경로와 실행 가능한 전송 작업을 구성합니다.
업무 환경이나 사용자 구성이 변경되면 동일한 정책 화면에서 접근 범위와 처리 기준을 조정할 수 있습니다.
Workspace Policy
│
┌─────┼────────────┐
▼ ▼ ▼
Users Groups Devices
│ │ │
▼ ▼ ▼
Access Operations Paths
│
▼
Transfer Rules

| 관리 항목 | 설정 내용 |
|---|---|
| Workspace | 사용자와 업무별 작업 공간 |
| Users / Groups | 작업 공간을 사용할 사용자와 그룹 |
| Devices | 연결 가능한 장비 |
| Paths | 파일을 사용할 경로 |
| Operations | 실행 가능한 파일 작업 |
| Transfer Rules | 파일 전송 기준과 처리 규칙 |
운영 관리
작업 공간별 전송 결과와 전체 운영 상태 함께 확인하기
각 작업 공간에서 실행되는 파일 전송과 처리 결과를 확인하고, 여러 작업 공간과 연결 장비의 상태를 중앙에서 함께 관리합니다.
운영자는 특정 작업 공간의 실행 상태를 확인하거나 전체 환경의 작업 현황을 기준으로 운영 상황을 파악할 수 있습니다.
Central Management
│
┌──────┼───────────────┐
▼ ▼ ▼
Workspace Devices Runs
│ │ │
└────────┼──────────────┘
▼
Operations View

작업 공간은 다음 흐름으로 구성하고 관리할 수 있습니다.
작업 공간 구성
│
▼
저장 위치 연결
│
▼
대상 장비 · 전송 경로 설정
│
▼
사용자 · 장비 접근 정책 구성
│
▼
전송 작업 실행
│
▼
공간별 · 전체 운영 상태 확인
개발자
작업 공간 식별자와 토큰을 요청에 실어 보내고 권한 응답 처리하기
인증 처리
토큰을 발급받고 만료 시 갱신하기
작업 공간 API는 액세스 토큰으로 인증합니다. 토큰이 없다면 계정으로 로그인해 발급받습니다.
import os
import threading
import requests
BASE_URL = os.getenv("INNORIX_BASE_URL", "https://app.innorix.com").rstrip("/")
def login(email, password):
response = requests.post(f"{BASE_URL}/api/auth/login",
json={"email": email, "password": password},
timeout=30)
response.raise_for_status()
# the login response nests the tokens under data.user
user = response.json()["data"]["user"]
return user["accessToken"], user.get("refreshToken")
def refresh(refresh_token):
response = requests.post(f"{BASE_URL}/api/auth/token/refresh",
headers={"X-Refresh-Token": refresh_token},
timeout=30)
response.raise_for_status()
# the refresh response nests the tokens directly under data
data = response.json()["data"]
return data["accessToken"], data.get("refreshToken")로그인과 갱신의 응답 구조가 다릅니다. 로그인은 data.user 아래, 갱신은 data 바로 아래입니다.
리프레시 토큰은 한 번만 유효합니다. 여러 스레드가 같은 토큰을 쓰는 서버 애플리케이션이라면 갱신이 동시에 일어나지 않도록 잠급니다.
class Session:
def __init__(self, email, password):
self._lock = threading.Lock()
self._access, self._refresh = login(email, password)
@property
def access_token(self):
with self._lock:
return self._access
def renew(self):
with self._lock:
self._access, self._refresh = refresh(self._refresh)
return self._access작업 공간 지정
모든 요청에 작업 공간 식별자를 함께 보내기
작업 공간은 요청 본문이 아니라 헤더로 전달됩니다. 호출부마다 직접 지정하면 누락될 수 있으므로 요청 조립을 한 곳으로 일원화합니다.
class Client:
def __init__(self, session, workspace_id=None):
self.session = session
self.workspace_id = workspace_id
def request(self, method, path, body=None, params=None, retried=False):
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.session.access_token}",
}
# when omitted the account's current workspace is used
if self.workspace_id:
headers["x-workspace-id"] = self.workspace_id
response = requests.request(method, BASE_URL + path, headers=headers,
json=body, params=params, timeout=30)
if response.status_code == 401 and not retried:
self.session.renew()
return self.request(method, path, body, params, retried=True)
return response작업 공간 식별자를 지정하지 않으면 계정의 현재 접근 가능한 공간이 사용됩니다. 단일 공간을 쓰는 애플리케이션은 지정하지 않아도 동작합니다.
토큰 만료도 이 지점에서 함께 처리합니다. 401을 받으면 한 번 갱신하고 재시도하되, 재시도한 요청이 다시 실패하면 그대로 올려보냅니다.
접근 범위 확인
계정의 조회 범위와 접근 가능한 장비 확인하기
me = api("GET", "/api/auth/me") or {}
print(me)
result = api("GET", "/api/devices", params={"page": 1, "size": 20}) or {}
if result.get("viewScope"):
print("view scope:", result["viewScope"])
for device in result.get("devices") or []:
print(device["deviceId"], device["name"])| 응답 항목 | 내용 |
|---|---|
devices | 작업 공간에 연결된 장비 목록 |
totalRows · lastPage | 전체 개수와 마지막 페이지 |
viewScope | 현재 계정의 조회 범위 |
장비 목록은 data.devices 배열로 반환됩니다. 응답의 viewScope로 계정이 전체를 보는지 일부만 보는지 확인할 수 있습니다.
권한 처리
401과 403을 구분해 재로그인과 화면 안내로 나누기
인증 실패와 권한 부족은 다르게 다뤄야 합니다. 전자는 재로그인, 후자는 화면 안내입니다.
| 상태 | 의미 | 처리 |
|---|---|---|
| 401 | 토큰이 없거나 유효하지 않음 | 갱신 후 재시도, 실패하면 재로그인 |
| 403 | 해당 작업 공간이나 경로에 접근 불가 | 사용자에게 안내 |
| 404 | 장비나 경로가 없음 | 대상이 사라졌음을 안내 |
| 429 | 요청이 과도함 | 잠시 후 재시도 |
class WorkspaceForbidden(Exception):
pass
def call(client, method, path, body=None, params=None):
response = client.request(method, path, body, params)
if response.status_code == 403:
raise WorkspaceForbidden(path)
payload = response.json() if response.content else {}
if not response.ok:
raise RuntimeError(payload.get("message") or f"HTTP {response.status_code}")
return payload.get("data")
try:
devices = call(client, "GET", "/api/devices")
except WorkspaceForbidden:
devices, notice = [], "no permission to access this workspace"권한 부족을 별도 예외로 분리하면 호출부에서 안내 문구로 변환할 수 있습니다. 같은 사용자라도 작업 공간에 따라 결과가 달라지므로, 화면 상단에 현재 작업 공간을 표시해 사용자가 빈 목록을 권한 문제로 오해하지 않게 합니다.
다중 공간 처리
여러 작업 공간을 다루는 코드에서 자격 정보 분리하기
작업 공간 식별자를 함수 인자로 전달하면 호출부 어딘가에서 누락됩니다. 공간마다 클라이언트를 만들고 그 객체를 전달합니다.
clients = {
workspace_id: Client(session, workspace_id)
for workspace_id in WORKSPACE_IDS
}
def collect_devices():
result = {}
for workspace_id, client in clients.items():
try:
result[workspace_id] = call(client, "GET", "/api/devices")
except WorkspaceForbidden:
continue
return result작업 공간 목록을 조회하는 엔드포인트는 없습니다. 후보를 넣고 접근 가능한 것만 걸러내는 방식으로 확인합니다.
고객사별로 계정이 다르다면 세션도 분리합니다. 하나의 세션을 여러 공간이 공유하는 구성과 섞이지 않도록 생성 지점을 나눕니다.
def build_client(config):
session = Session(config["email"], config["password"])
return Client(session, config["workspace_id"])
clients = {name: build_client(cfg) for name, cfg in TENANTS.items()}여러 작업 공간을 순회하는 배치는 한 공간의 실패가 전체를 멈추지 않게 합니다.
failed = []
for name, client in clients.items():
try:
run_for(client)
except Exception as error:
failed.append((name, error))
for name, error in failed:
print(f"{name}: {error}")| 관리 항목 | 코드에서 다루는 내용 |
|---|---|
| 세션 | 계정별 액세스·리프레시 토큰 |
| 작업 공간 | 요청 헤더의 공간 식별자 |
| 권한 | 403 응답과 화면 안내 |
| 자원 | 공간별 장비와 조회 범위 |
| 실행 | 공간 안에서 생성한 전송 |