파일 작업 공간

업무 팀IT 엔지니어개발자

시작하기

기본 개념

파일 전송을 작업 공간별로 분리해 관리하기

파일 작업 공간은 특정 업무에서 사용하는 파일, 저장 위치, 전송 대상, 접근 범위를 하나의 공간으로 구성하는 방식입니다.

예를 들어 개발팀, 콘텐츠팀, 데이터 분석팀이 각각 사용하는 파일을 별도의 작업 공간으로 구성하고, 각 공간마다 필요한 저장 위치와 전송 장비를 연결할 수 있습니다.

text
File Workspace
      │
 ┌────┼───────────────┐
 ▼    ▼               ▼
Files Storage       Access
 │       │             │
 ▼       ▼             ▼
Transfer Targets   Users / Devices

이렇게 구성하면 여러 업무에서 사용하는 파일과 전송 환경을 작업 공간별로 구분하고 관리할 수 있습니다.

공간 흐름

파일 저장부터 전송과 결과 확인까지 이어가기

사용자는 접근 권한이 설정된 작업 공간을 선택하고 필요한 파일을 저장하거나 확인합니다.

작업 공간에 연결된 전송 작업을 실행하면 선택한 파일이 지정된 장비와 업무 환경으로 전송되며, 실행 결과도 같은 공간에서 확인할 수 있습니다.

text
작업 공간 선택
      │
      ▼
파일 저장 · 확인
      │
      ▼
전송 작업 실행
      │
      ▼
대상 장비 반영
      │
      ▼
처리 결과 확인

분리 효과

여러 업무와 장비의 파일을 목적에 맞게 관리하기

업무별로 작업 공간을 구성하면 파일과 저장 위치, 전송 대상, 접근 범위를 하나의 관리 기준으로 정리할 수 있습니다.

구분개별 관리작업 공간 관리
파일여러 위치의 파일을 각각 확인업무별 파일을 공간별로 관리
저장 위치파일마다 위치 확인작업 공간별 저장 위치 구성
전송 대상작업마다 대상 확인공간별 전송 장비와 경로 연결
접근 범위사용자별 파일 확인작업 공간 기준으로 접근 범위 관리
결과 확인작업별 결과 확인공간별 실행 결과 확인

업무팀

필요한 파일을 작업 공간에서 관리하고 활용하기

공간 선택

업무에 맞는 파일과 작업을 한곳에서 확인하기

업무를 시작할 때 필요한 파일과 작업이 구성된 작업 공간을 선택합니다.

작업 공간에는 해당 업무에서 사용하는 파일과 연결된 전송 작업이 함께 구성되어 있어, 담당자는 현재 업무에 필요한 공간에서 파일 작업을 진행할 수 있습니다.

text
My Workspaces
     │
     ├── Project A
     │      ├── Files
     │      └── Transfers
     │
     ├── Media Team
     │      ├── Files
     │      └── Transfers
     │
     └── Data Analysis
            ├── Files
            └── Transfers

파일 활용

필요한 파일을 저장하고 다음 업무에 활용하기

작업 공간에서 업무에 필요한 파일을 저장하고, 현재 작업에 사용할 파일을 확인합니다.

저장된 파일은 해당 작업 공간에 연결된 업무 흐름과 함께 활용할 수 있어, 담당자는 필요한 파일을 선택해 다음 작업을 이어갈 수 있습니다.

text
Workspace
    │
    ├── Upload Files
    │
    ├── Browse Files
    │
    └── Select Files
            │
            ▼
        Next Work

전송 실행

준비된 전송 작업으로 파일 보내기

필요한 파일을 선택한 후 작업 공간에 구성된 전송 작업을 실행합니다.

담당자는 현재 업무에서 사용할 파일과 실행할 작업을 선택하고, 진행 상태와 처리 결과를 확인할 수 있습니다.

text
Select Files
      │
      ▼
Select Transfer
      │
      ▼
Run
      │
 ┌────┴────┐
 ▼         ▼
Progress   Target
 │         │
 └────┬────┘
      ▼
   Result

업무팀은 작업 공간에서 필요한 파일을 관리하고 준비된 전송 흐름을 활용해 다음 업무까지 이어갈 수 있습니다.

IT 엔지니어

파일 작업 공간과 전송 환경을 구성하고 중앙에서 관리하기

공간 구성

업무별 파일 저장 위치와 관리 범위 구성하기

업무 목적에 따라 작업 공간을 생성하고, 각 공간에서 사용할 파일 저장 위치와 관리 범위를 구성합니다.

예를 들어 개발 결과물, 미디어 파일, 데이터 처리 결과를 각각 별도의 작업 공간으로 구성할 수 있습니다.

text
Organization
      │
 ┌────┼───────────────┐
 ▼    ▼               ▼
Dev  Media           Data
 │     │               │
 ▼     ▼               ▼
Storage Storage      Storage

작업 공간과 대상 장비, 파일 경로 연결하기

각 작업 공간에 사용할 전송 대상 장비와 파일 경로를 연결합니다.

서버, 스토리지, 업무 장비를 작업 공간에 연결하고 파일 전송 경로를 설정해 업무별 전송 환경을 구성할 수 있습니다.

text
Workspace
     │
     ├── Source Storage
     │
     ├── Transfer Flow
     │
     └── Target Devices
             │
      ┌──────┼──────┐
      ▼      ▼      ▼
   Server  Storage  System

접근 정책

사용자와 장비별 접근 범위와 전송 기준 관리하기

작업 공간마다 사용자와 그룹의 접근 범위, 연결 가능한 장비, 파일 경로와 실행 가능한 전송 작업을 구성합니다.

업무 환경이나 사용자 구성이 변경되면 동일한 정책 화면에서 접근 범위와 처리 기준을 조정할 수 있습니다.

text
Workspace Policy
       │
 ┌─────┼────────────┐
 ▼     ▼            ▼
Users Groups      Devices
 │       │            │
 ▼       ▼            ▼
Access  Operations  Paths
             │
             ▼
       Transfer Rules

관리 항목설정 내용
Workspace사용자와 업무별 작업 공간
Users / Groups작업 공간을 사용할 사용자와 그룹
Devices연결 가능한 장비
Paths파일을 사용할 경로
Operations실행 가능한 파일 작업
Transfer Rules파일 전송 기준과 처리 규칙

운영 관리

작업 공간별 전송 결과와 전체 운영 상태 함께 확인하기

각 작업 공간에서 실행되는 파일 전송과 처리 결과를 확인하고, 여러 작업 공간과 연결 장비의 상태를 중앙에서 함께 관리합니다.

운영자는 특정 작업 공간의 실행 상태를 확인하거나 전체 환경의 작업 현황을 기준으로 운영 상황을 파악할 수 있습니다.

text
Central Management
        │
 ┌──────┼───────────────┐
 ▼      ▼               ▼
Workspace Devices      Runs
 │        │              │
 └────────┼──────────────┘
          ▼
     Operations View

작업 공간은 다음 흐름으로 구성하고 관리할 수 있습니다.

text
작업 공간 구성
       │
       ▼
저장 위치 연결
       │
       ▼
대상 장비 · 전송 경로 설정
       │
       ▼
사용자 · 장비 접근 정책 구성
       │
       ▼
전송 작업 실행
       │
       ▼
공간별 · 전체 운영 상태 확인

개발자

작업 공간 식별자와 토큰을 요청에 실어 보내고 권한 응답 처리하기

인증 처리

토큰을 발급받고 만료 시 갱신하기

작업 공간 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 응답과 화면 안내
자원공간별 장비와 조회 범위
실행공간 안에서 생성한 전송