Copyright © 2026 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
Linked Web Storage Protocol 명세는 애플리케이션이 상호 운용 가능한 방식으로 외부에 저장된 데이터에 안전하고 권한이 부여된 접근을 할 수 있도록 하는 것을 목표로 한다.
이 절은 발행 당시 이 문서의 상태를 설명한다. 현재 W3C 발행물과 이 기술 보고서의 최신 개정판 목록은 W3C 표준 및 초안 색인에서 확인할 수 있다.
이는 비공식 제안이다.
이 문서는 Linked Web Storage Working Group에서 권고안 트랙을 사용하여 작업 초안으로 발행했다.
작업 초안으로의 발행은 W3C와 그 회원들의 승인을 의미하지 않는다.
이는 초안 문서이며 언제든지 다른 문서로 갱신, 대체 또는 폐기될 수 있다. 이 문서를 진행 중인 작업이 아닌 다른 것으로 인용하는 것은 적절하지 않다.
이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성했다. W3C는 이 그룹의 산출물과 관련하여 이루어진 특허 공개의 공개 목록을 유지하며, 해당 페이지에는 특허 공개 지침도 포함되어 있다. 개인이 필수 청구항을 포함한다고 믿는 특허를 실제로 알고 있는 경우, 그 개인은 W3C 특허 정책 제6절에 따라 해당 정보를 공개해야 한다.
이 문서는 2025년 8월 18일 W3C 프로세스 문서의 적용을 받는다.
이 절은 비규범적입니다.
이 명세는 Linked Web Storage (LWS) 프로토콜을 정의하며, 이 프로토콜은 클라이언트 애플리케이션이 에이전트의 신원과 권한에 따라 웹 리소스에 접근하고 관리할 수 있도록 합니다.
리소스 서버가 연결된 리소스의 계층을 관리하고 이에 대한 접근을 제공하는 방식을 표준화함으로써, 이 프로토콜은 사용자가 서로 다른 클라이언트 애플리케이션으로 동일한 저장 데이터와 상호작용할 수 있도록 합니다. 이러한 연결된 리소스는 컨테이너와 포함 관계를 통해 정의되며, 이러한 관계는 항목이 어디에 위치하는지 설명하고, 메타데이터는 리소스 간의 관계를 설명합니다.
이 프로토콜은 이러한 리소스에 대한 생성, 읽기, 갱신 및 삭제의 표준 작업을 정의합니다. 각 리소스는 URI로 식별됩니다. 클라이언트는 루트 컨테이너에서 리소스 계층을 탐색하며, 서버 응답에 제공된 링크를 통해 포함된 리소스와 그 관계를 발견합니다.
에이전트의 신원은 Linked Web Storage 서버 외부에 있을 수 있는 신원 제공자를 통해 확인됩니다. 이러한 느슨한 결합은 서버가 자격 증명을 반드시 직접 관리하는 것은 아니라는 의미이며, 대신 서버는 신뢰할 수 있는 신원 제공자가 발급한 토큰인 서명된 인증 자격 증명을 수신하고 검증할 수 있습니다. 따라서 사용자는 기존 신원을 준수 서버에 제시할 수 있으며, 항상 해당 서버만을 위한 신규 또는 기존 계정을 필요로 하지는 않습니다. LWS는 사용자 인증을 활용하며, 이는 예를 들어 OpenID Connect, SAML 2.0 및 자체 서명된 제어 식별자(CIDs) 명세에서 정의됩니다.
인가는 요청 에이전트가 리소스에 접근하거나 해당 리소스에 대한 작업을 수행할 권한이 있는지를 결정합니다. 준수 LWS 서버는 각 리소스에 대해 리소스 관리자를 지정하며, 이 관리자는 리소스가 비공개인지(즉, 소유자만 이용 가능), 제한되는지(즉, 정의된 사용자 집합만 이용 가능), 공개인지(즉, 모든 사용자가 이용 가능)를 결정합니다. 서버는 각 클라이언트 요청을 처리할 때 리소스 관리자의 접근 결정을 적용합니다.
이 명세는 다음 사용자를 대상으로 합니다:
서로 다른 LWS 서버 인스턴스는 각각 연결된 리소스의 자체 계층을 유지합니다. 에이전트는 동일한 식별 정보를 사용하여 여러 서버를 탐색할 수 있으며, 각 서버 리소스에 대한 접근은 해당 서버의 리소스 관리자가 부여한 권한에 따라 결정됩니다. 모든 LWS 서버 인스턴스가 가능한 모든 에이전트 식별자를 지원한다고 보장되지는 않습니다. 특정 LWS 서버 인스턴스는 에이전트에게 특정 신원 제공자를 통해 자신을 식별하도록 요구할 수 있으며, 이는 해당 서버 인스턴스가 관리하는 리소스에 접근하기 위한 것입니다.
LWS 프로토콜은 어떤 주체가 일부 리소스를 다음 대상에게 제공할 수 있는 표준 상호작용을 정의합니다: 에이전트.
리소스 관리자는 제공 리소스를 비공개로 유지하거나 누구나 이용할 수 있도록 공개하거나, 다음과 같은 제한된 집합에만 표시되도록 제한할 수 있습니다: 요청 에이전트.
비규범적으로 표시된 절뿐만 아니라, 이 명세의 모든 작성 지침, 다이어그램, 예시 및 참고 사항도 비규범적입니다. 이 명세의 나머지 모든 내용은 규범적입니다.
키워드 MAY, MUST, MUST NOT, OPTIONAL, RECOMMENDED, REQUIRED, SHOULD 및 SHOULD NOT를 이 문서에서는 다음에 설명된 대로 해석합니다: BCP 14 [RFC2119] [RFC8174] 단, 여기에 표시된 것처럼 모두 대문자로 나타나는 경우에만 해당합니다.
LWS 서버는 HTTP 서버이며 [rfc9112], 다음을 준수합니다: 이 명세의 모든 관련 "MUST" 문장. 구체적으로, 다음 절의 관련 규범적 "MUST" 문장을 9. 작업에서 반드시 준수해야 합니다.
LWS 클라이언트는 HTTP 클라이언트이며 [rfc9112], 이 명세의 모든 관련 "MUST" 문장을 준수합니다. 구체적으로, 관련 규범적 "MUST" 문장을 이 문서의 9. 작업에서 반드시 준수해야 합니다.
다음 다이어그램은 플랫폼 독립적인 모델로, 구현을 안내합니다.
"인가 서버"와 "클라이언트"라는 용어는 OAuth 2.0 인가 프레임워크에 정의되어 있습니다 [RFC6749].
"최종 사용자"와 "발급자"라는 용어는 OpenID Connect Core 1.0에 정의되어 있습니다 [OPENID-CONNECT-CORE].
이 명세는 다음 용어를 정의합니다:
conformsTo 속성에서 해당 프로파일을 참조하여 나타냅니다.
저장소 설명 리소스에 있는 객체입니다.
https://www.w3.org/ns/lws#StorageResource로 식별됩니다.
이 절은 다음 대상을 식별하는 메커니즘을 정의합니다: 에이전트 및 최종 사용자, 이들은 Linked Web Storage 서버와 상호작용합니다. 이 명세는 특정한 형식을 의무화하지 않습니다: 인증 자격 증명의 형식입니다. 다만, 기존 신원 시스템을 Linked Web Storage 인가 프레임워크와 함께 사용할 수 있는 방법을 설명합니다.
이 절에 설명된 데이터 모델은 모든 구체적인 직렬화에 대한 요구 사항을 개괄합니다: 인증 자격 증명의 직렬화입니다.
인증 자격 증명은 반드시 주체에 관한 변조를 감지할 수 있는 클레임을 포함해야 하며, 다음을 포함합니다:
다음의 유효성 검사에는 인증 자격 증명, 신뢰 관계가 필요하며, 이는 자격 증명의 검증자와 발급자 간의 관계입니다. 이 신뢰 관계는 허용됩니다, 수립하는 것이 대역 외 메커니즘을 통해서도 가능합니다. 신뢰를 수립하기 위한 추가 메커니즘은 검증자와 발급자 사이에서 사용되며, 특정 인증 스위트에 개괄되어 있습니다.
인증 자격 증명은 반드시 서명되어야 합니다. 권장되는 서명 방식은 비대칭 암호화를 사용하는 것입니다.
각 인증 스위트는 반드시 토큰 유형 URI와 연결되어야 합니다. 인증 스위트는 가급적 IANA "OAuth URI" 레지스트리에 정의된 URI를 사용하는 것이 좋습니다.
저장소 설명 리소스는 클라이언트가 사용할 수 있는 정보를 제공합니다. 다음과 상호작용할 때 사용하는 정보로, 저장소, 기능과 서비스 엔드포인트에 대한 설명을 포함합니다.
저장소 설명 리소스는 W3C 제어 식별자 문서를 특수화한 문서입니다 [CID-1.0]. 이 리소스는 다음에 정의된 데이터 모델을 준수하며 [CID-1.0], LWS 전용 속성으로 확장됩니다.
id - id 속성은 필수입니다. 그 값은 반드시 정규 URI여야 하며,
다음의 URI입니다: 저장소.
다음에 따라 [CID-1.0], 이 URI는 또한 정규
URL이며, 제어 식별자 문서의 URL입니다.
type - type 속성은 필수입니다. 그 값은
반드시 문자열이어야 하며,
다음과 같아야 합니다:
Storage. 또는 Storage와 같은 항목을 포함하는 문자열 집합이어야 합니다.
(대소문자를 구분합니다).
capability - capability 속성은 선택
사항입니다. 그 값은 반드시 기능의 집합이어야 하며,
각 기능은 다음 속성을 포함하는 맵으로 설명됩니다.
추가 속성이 선택적으로 존재할 수 있습니다.
id - id 속성은 선택
사항입니다. 존재하면 그 값은 반드시
URI여야 합니다.type - type 속성은 필수입니다. 그 값은 반드시 문자열
또는 문자열 집합이어야 합니다.service - service 속성은 필수입니다.
그 값은 반드시 서비스의 집합이어야 하며,
각 서비스는 다음 속성을 포함하는 맵으로 설명됩니다. 추가
속성이 선택적으로 존재할 수 있습니다.
이 집합은 반드시 다음과 같은 type을 가진 서비스를 포함해야 합니다:
StorageRoot.
id - id 속성은 선택
사항입니다. 존재하면 그 값은 반드시
URI여야 합니다.type - type 속성은 필수입니다. 그 값은 반드시 문자열
또는 문자열 집합이어야 합니다.serviceEndpoint - serviceEndpoint
속성은 필수입니다. 그 값은 반드시
URI여야 합니다.{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"service": [{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
}]
}
GET 및 HEAD 요청이 다음 리소스를 대상으로 할 때, 저장소 리소스, 모든 응답은 반드시 다음을 포함해야 합니다:
Link 헤더, 그 대상은 다음의 정규 URI여야 합니다: 저장소. 이 헤더에는
관계
(rel) 매개변수가 포함되며, 그 값은 https://www.w3.org/ns/lws#storage와 같아야 합니다.
다음에 대한 요청은 저장소 URI, 반드시 다음을
준수하는 문서를 반환해야 합니다:
저장소 설명 리소스 데이터 모델,
미디어 유형은 application/lws+cid여야 합니다.
콘텐츠 협상으로 다른 형식이 요구되는 경우는 예외입니다.
저장소 설명은 추가 기능에 대한 설명을 포함할 수 있으며, 다음이 지원하는 기능입니다: 저장소.
저장소가
클라이언트의 up 링크 변경을 허용하는 경우,
다음의 링크입니다: 저장소 리소스,
이를 통해 다음 간에 리소스를 이동할 수 있으며, 컨테이너, 이는 다음에 설명되어 있습니다:
9.6 리소스 이동
(컨테이너 간), 반드시 다음과 같은 type을 가진 기능을 설명해야 합니다:
해당 값은
MoveResource입니다. 클라이언트는 절대로 up
링크가 수정 가능하다고 가정해서는 안 됩니다.
이 기능이 없는 경우에 적용됩니다. 다음 속성은
MoveResource 기능에 대해 정의됩니다:
appliesTo - appliesTo 속성은 선택
사항입니다. 존재하면 그 값은 반드시
다음 유형의 이름을 나타내는 문자열 집합이어야 합니다: 저장소 리소스,
이 기능이 적용되는 유형이며, 예를 들면
DataResource 또는 Container입니다. 이 속성이 없으면 기능은
적용됩니다.
다음에만 적용됩니다: 데이터 리소스.
preservesIdentifier - preservesIdentifier
속성은 선택 사항입니다.
존재하면 그 값은 반드시 다음의 보존 여부를 나타내는 불리언이어야 합니다: 저장소가 식별자를 보존하는지,
이동한 리소스의 식별자를 보존하는지 나타냅니다. 이 속성이 없으면 클라이언트는 절대로 가정해서는 안 됩니다.
식별자가
보존된다고 가정해서는 안 되며, 반드시 갱신에 대한 응답에 의존하여
다음의 식별자를
결정해야 합니다: 리소스.
{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"capability": [{
"type": "MoveResource",
"appliesTo": ["DataResource", "Container"],
"preservesIdentifier": true
}],
"service": [{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
}]
}
저장소 설명 리소스는 반드시
StorageRoot 서비스를 포함해야 하며,
그 serviceEndpoint는 다음의 URI입니다: 저장소 루트 컨테이너.
다음에 연결된 추가 서비스도 저장소, 선택적으로 존재할 수 있습니다.
이러한 추가 서비스의 API 정의는 이 명세의 범위에 포함되지 않습니다.
저장소 설명은 반드시 미디어 유형
application/lws+cid로 직렬화할 수 있어야 합니다.
@context 속성은 반드시 다음 URI로 시작하는 배열이어야 합니다:
https://www.w3.org/ns/cid/v1
및 https://www.w3.org/ns/lws/v1.
콘텐츠 협상을 통해 다른 표현도 선택적으로 이용할 수 있습니다.
{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"capability": [{
"type": "https://feature.example/PatchSupport",
"format": {
"text/turtle": ["application/sparql-update"],
"application/n-triples": ["application/sparql-update"],
"application/linkset+json": ["application/json-patch+json", "application/merge-patch+json"]
}
}, {
"type": "https://feature.example/ResumableUploads"
}, {
"type": "https://feature.example/ContentNegotiation",
"source": "application/ld+json",
"target": ["text/turtle", "application/n-triples"]
}, {
"type": "https://feature.example/ContentNegotiation",
"source": "image/jpeg",
"target": ["image/png"]
}],
"service": [{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
}, {
"type": "NotificationService",
"serviceEndpoint": "https://storage.example/notification/api",
"subscriptionType": ["WebhookSubscription"]
}, {
"type": "TypeIndexService",
"serviceEndpoint": "https://storage.example/types/index"
}, {
"type": "TypeSearchService",
"serviceEndpoint": "https://storage.example/types/search"
}, {
"type": "DataSharingService",
"serviceEndpoint": "https://storage.example/sharing/api"
}]
}
Linked Web Storage는 리소스를 다음으로 구성합니다: 컨테이너. 컨테이너는 다른 리소스에 대한 참조를 보유하는 특수한 리소스로, 해당 리소스를 멤버라고 합니다. 컨테이너는 디렉터리나 컬렉션과 유사한 구성 단위로서, 클라이언트가 리소스를 그룹화하고 발견하며, 탐색할 수 있도록 합니다. 컨테이너는 멤버 리소스에 대한 참조를 유지하며, 여기에는 컨테이너가 아닌 리소스와 추가 컨테이너 리소스가 모두 포함될 수 있어 계층적 구성이 가능합니다. 일반적으로 컨테이너 자체의 고유 콘텐츠는 최소한이며, 메타데이터나 멤버의 열거를 제외하면 거의 없습니다. 주된 역할은 하위 리소스를 모으고 구조화하는 것입니다. 저장소 시스템의 루트는 컨테이너로 지정되며, 상위 부모가 없는 최상위 구성 단위로 기능합니다. 다음의 구조는 컨테이너 표현, 다음에 정의되어 있습니다: 8.1 컨테이너 표현. 미디어 유형과 페이지 나누기 요구 사항은 다음에 정의되어 있습니다: 12. LWS 미디어 유형. 저장소는 선택적으로 루트 컨테이너로 기능하여 직접 쓰기를 허용할 수 있습니다.
모든 LWS 저장소에는 저장소 루트가 있으며, 최상위 구성 단위로 기능합니다. 저장소 루트는 부모가 없으며 다음에 대한 진입점 역할을 합니다: 저장소 계층.
LWS의 리소스는 다음 중 하나로 분류됩니다:
포함
관계는 리소스와 그 부모 컨테이너 간의 관계이며, rel="up" 링크
관계로 표현됩니다. 서버는 반드시 Link 헤더를 포함해야 하며,
rel="up"이 부모 컨테이너를 가리켜야 합니다. 이는 모든
루트가 아닌 리소스에 대한 GET 및 HEAD 요청의 응답에 적용됩니다.
Link: </alice/notes/>; rel="up"
컨테이너의 멤버는 해당 표현에 나열되며,
items 속성을 사용합니다. 서버가 이 목록을 관리하며, 클라이언트는 직접 수정할 수 없습니다. 멤버십
변경은 리소스 생성과 삭제의 부수 효과로 발생하며, 서버가 다음을 허용하는 경우에도 발생합니다:
리소스의 up 링크 수정. 이때 다음 간의 리소스 이동으로 발생합니다: 컨테이너, 이는 다음에 설명되어 있습니다: 9.6 컨테이너 간 리소스 이동.
서버는 반드시 다음을 유지해야 합니다: 포함 관계의 무결성을 항상 유지해야 합니다:
items 목록에 추가합니다.
items 목록.
다음을 삭제하려면 컨테이너, 해당 컨테이너는
비어 있어야 합니다. 단, 재귀적 삭제를 명시적으로 요청한 경우는 예외입니다.up 링크가 변경되면 서버는 반드시 해당 리소스를
items 목록에서 원자적으로 제거해야 합니다.
이전 부모의 목록이며, 컨테이너의 목록입니다. 그리고 items 목록에 추가해야 합니다.
대상 컨테이너의 목록입니다. up 링크 변경 지원은
선택 사항이며, 그에 따른 요구 사항은 다음에 정의되어 있습니다: 9.6 리소스 이동
(컨테이너 간).
리소스는 URI로 식별됩니다. 리소스의 URI는 다음에서의 위치와 독립적입니다: 포함 관계 계층. 서버는 리소스를 생성할 때 URI를 할당하며, 선택적으로 클라이언트의 힌트를 반영할 수 있습니다. 하지만 클라이언트는 가급적 URI 구조가 포함 관계를 반영한다고 가정하지 않는 것이 좋습니다.
포함
관계는 메타데이터를 통해 표현됩니다.
(rel="up" 링크 및 items 속성, 다음에 있는 속성입니다: 컨테이너
표현), URI 경로 구조로 표현되지 않습니다. 이러한 분리는 서버에 URI
할당의 유연성을 제공하면서 명확하게 정의된 구성 모델을 유지할 수 있도록 합니다.
LWS 컨테이너 표현은 컨테이너와 그 내용을 설명합니다. 이 절은
필수 속성과
선택 속성을 정의하며, application/lws+json으로 제공되는 경우에 적용됩니다. 다른 RDF 미디어
유형의 표현은 콘텐츠 협상을 통해 요청할 수 있습니다.
컨테이너 표현은 반드시 다음 속성을 포함해야 합니다:
id: 다음의 URI입니다: 컨테이너.type: 값은 "Container"입니다.totalItems: 포함된 리소스의 총수를 나타내는 정수이며,
다음에 포함된 리소스 중 컨테이너, 클라이언트에 공개할 수 있는 리소스의 수입니다. 이
개수는 가급적 정확한 것이 좋지만, 선택적으로
근삿값일 수 있습니다.items: 포함된 리소스 설명의 배열입니다(아래 참조). 해당
대상인
컨테이너가 비어 있으면 이는 반드시
빈 배열이어야 합니다. 다음의 컨테이너 목록이
페이지로 나뉘면 items에는 현재 페이지의 리소스만 포함됩니다. 자세한 내용은 페이지 나누기를
참조하세요.
items 배열의 각 항목은 다음에 포함된 리소스를 설명합니다: 컨테이너. 포함된 리소스 설명은 반드시 다음을 포함해야 합니다:
id: 포함된 리소스의 URI입니다.type: 리소스의 유형입니다. 반드시 다음이어야 합니다:
"DataResource" 또는 "Container", 또는
이 두 문자열 중 하나 이상을 포함하는 배열입니다. 서버는 추가 사용자 정의 유형을 선택적으로
URI로 포함할 수 있습니다(예: ["DataResource", "http://example.org/customType"]).
포함된 리소스 설명은 가급적 다음을 포함하는 것이 좋습니다:
format: 리소스의 미디어 유형입니다(예:
"text/plain", "image/jpeg"). 반드시 존재해야 합니다.
DataResources에 적용됩니다.
size: 정수로 표현한 리소스의 크기이며, 단위는 바이트입니다.
modified: 리소스를 마지막으로 수정한 날짜와 시각이며,
ISO 8601 날짜·시간 문자열로 표현합니다.다음 예시는 컨테이너를 보여주며, 위치는
https://storage.example/alice/notes/이고 두 리소스를 포함합니다. 다른 탭은
동일한 컨테이너를 다른 RDF 미디어 유형으로 보여주며, 다음에 속하는 유형입니다: LWS 프로파일.
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/notes/",
"type": "Container",
"totalItems": 2,
"items": [
{
"type": "DataResource",
"id": "/alice/notes/shoppinglist.txt",
"format": "text/plain",
"size": 47,
"modified": "2025-11-24T12:00:00Z"
},
{
"type": ["DataResource", "http://example.org/customType"],
"id": "/alice/notes/todo.json",
"format": "application/json",
"size": 2048,
"modified": "2025-11-24T13:00:00Z"
}
]
}
@prefix lws: <https://www.w3.org/ns/lws#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix schema: <http://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<> a lws:Container ;
lws:totalItems 2 ;
lws:items <shoppinglist.txt>, <todo.json> .
<shoppinglist.txt> a lws:DataResource ;
dcterms:format "text/plain" ;
schema:size "47"^^xsd:long ;
dcterms:modified "2025-11-24T12:00:00Z"^^xsd:dateTime .
<todo.json> a lws:DataResource, <http://example.org/customType> ;
dcterms:format "application/json" ;
schema:size "2048"^^xsd:long ;
dcterms:modified "2025-11-24T13:00:00Z"^^xsd:dateTime .
<https://storage.example/alice/notes/> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://www.w3.org/ns/lws#Container> .
<https://storage.example/alice/notes/> <https://www.w3.org/ns/lws#totalItems> "2"^^<http://www.w3.org/2001/XMLSchema#integer> .
<https://storage.example/alice/notes/> <https://www.w3.org/ns/lws#items> <https://storage.example/alice/notes/shoppinglist.txt> .
<https://storage.example/alice/notes/> <https://www.w3.org/ns/lws#items> <https://storage.example/alice/notes/todo.json> .
<https://storage.example/alice/notes/shoppinglist.txt> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://www.w3.org/ns/lws#DataResource> .
<https://storage.example/alice/notes/shoppinglist.txt> <http://purl.org/dc/terms/format> "text/plain" .
<https://storage.example/alice/notes/shoppinglist.txt> <http://schema.org/size> "47"^^<http://www.w3.org/2001/XMLSchema#long> .
<https://storage.example/alice/notes/shoppinglist.txt> <http://purl.org/dc/terms/modified> "2025-11-24T12:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
<https://storage.example/alice/notes/todo.json> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://www.w3.org/ns/lws#DataResource> .
<https://storage.example/alice/notes/todo.json> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/customType> .
<https://storage.example/alice/notes/todo.json> <http://purl.org/dc/terms/format> "application/json" .
<https://storage.example/alice/notes/todo.json> <http://schema.org/size> "2048"^^<http://www.w3.org/2001/XMLSchema#long> .
<https://storage.example/alice/notes/todo.json> <http://purl.org/dc/terms/modified> "2025-11-24T13:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
이 절은 Linked Web Storage (LWS) 서버가 반드시 지원해야 하는 네 가지 핵심 작업을 정의합니다. 이러한 작업은 리소스와 컨테이너를 전송 방식에 독립적으로 조작하며, 구현 세부 사항보다 의미론에 초점을 맞춥니다. 각 작업은 입력, 예상 동작 및 가능한 응답을 명시합니다. 응답에는 성공 표시, 리소스 표현(해당하는 경우) 및 오류 조건이 포함됩니다. 구현은 반드시 이러한 작업을 원자적이고 일관되게 처리해야 하며, 이는 각 작업이 완전히 성공하거나 부분적인 부수 효과 없이 실패해야 한다는 의미입니다. 오류가 발생하면 응답은 가급적 다음 대상에게 충분한 세부 정보를 제공하는 것이 좋습니다: 에이전트, 민감한 정보를 유출하지 않으면서 문제를 이해할 수 있도록 합니다.
각 핵심 작업(생성, 읽기, 갱신, 삭제)에 대해 사용할 HTTP 메서드, 필수
헤더 또는 기타 특별 고려 사항(콘텐츠 협상, 컨테이너 목록의 페이지 나누기
및 조건부 요청 포함), 서버가 수행하고 반환해야 하는 내용을 설명합니다. 표준 HTTP 상태 코드를 사용하여
결과를 나타내며, 할당량 초과와 같은 상황에 대한 추가 매핑을 제공합니다.
(507 Insufficient Storage) 또는 사전 조건 실패(412 Precondition Failed)입니다. 이
바인딩은 HTTP/1.1과 관련 RFC를 준수하려고 합니다(예를 들어 [RFC7231]은 HTTP
의미론, [RFC7233]은 범위
요청, [RFC5789]은 PATCH, [RFC8288]은 웹 링크,
[RFC9264]은 링크
집합에 해당합니다). 이를 통해 웹 표준과 자연스럽게 통합됩니다. 발견 가능성은 다음과 같은 메커니즘으로 강조됩니다:
Link 헤더와 401 응답의 WWW-Authenticate 헤더,
하드코딩된 URI 위치를 피합니다. 모든 작업에서 메타데이터 통합이 요구되며, 원자성과
서버 관리 속성 및 사용자 관리 속성에 대한 링크 집합 사용을 보장합니다.
참고: 이 명세의 모든 예시와 마찬가지로, 이 절의 예시(HTTP 요청 및 응답 조각)는 비규범적이며 일반적인 사용법을 보여주기 위한 것입니다. 실제 요구 사항은 설명 본문과 표에 명시되어 있습니다. 또한 이 바인딩은 HTTP를 다루지만(최초 대상 프로토콜), 원칙적으로 향후 LWS 작업을 다른 프로토콜에 바인딩할 수도 있습니다. 다음의 미디어 유형 요구 사항은 컨테이너 표현, 다음에 정의되어 있습니다: 12. LWS 미디어 유형.
이 절은 메타데이터를 다음과 연결하는 모델을 정의합니다: 저장소 리소스. LWS 메타데이터 시스템은 웹 링크의 원칙에 기반합니다 [RFC8288]. 이를 통해 서버는 유형이 지정된 링크로 리소스 간의 관계를 설명할 수 있습니다. 메타데이터는 발견 가능성을 높이고 자기 설명적인 API를 지원하며, 리소스 작업 및 컨테이너 계층과 일치합니다.
메타데이터 모델 LWS의 모든 메타데이터는 리소스(링크 맥락)에서 시작하는 유형이 지정된 링크의 집합으로 표현됩니다. 각 링크는 다음으로 구성됩니다:
메타데이터는 리소스와 해당 표현을 구분하며, 해당하는 경우 여러 미디어 유형을 허용합니다. 다음의 경우, 데이터 리소스, 메타데이터에는 표현이 포함되며 각 표현에는 format과 선택적 sizeInBytes가 있습니다. 다음의 경우, 컨테이너 및 데이터 리소스, 부모를 가리키는 링크인 컨테이너 리소스 링크를 해당 리소스의 메타데이터에 포함되는 것으로 간주합니다.
링크 집합 리소스 다음의 각 리소스에 대해, 저장소, 서버는 반드시 메타데이터 링크를 독립된 리소스로 제공해야 하며, 다음을 따라야 합니다: [RFC9264].
rel="linkset".
application/linkset+json으로 이용할 수 있어야 합니다.
메타데이터 발견 클라이언트는 주로 GET 또는 HEAD 요청에 대한 응답의 Link 헤더를 통해 메타데이터를 발견합니다.
rel="https://www.w3.org/ns/lws#storage", 관련 응답에 포함합니다.
rel="up"이 있는 Link 헤더를 포함해야 하며, 부모는 컨테이너입니다. 모든 루트가
아닌 리소스에 적용됩니다.https://www.w3.org/ns/lws#PreferLinkRelations, 특정
관계를 포함하거나 생략할 수 있습니다.
메타데이터 유형
| 범주 | 설명 |
|---|---|
| 시스템 관리 | 서버가 유지 관리하며 읽기 전용입니다. linkset, type,
format, size, modified를 포함합니다.
|
| 핵심 메타데이터 | 클라이언트가 관리합니다(서버의 제한을 따릅니다). up,
items, title, creator를 포함합니다.
|
| 사용자 정의 | 사용자가 생성한 사용자 정의 어휘와 색인입니다. |
수정 가능성 고려 사항 클라이언트는 핵심 메타데이터를 선택적으로 수정할 수 있습니다. 상호 운용성을 보장하려면, 서버는 반드시 표준 HTTP 헤더를 사용하여 기능을 알려야 합니다:
메서드 발견: 서버는 반드시 GET 및 PATCH 작업 지원을 알려야 하며, 다음에 대한 작업입니다: 링크 집합 리소스. Allow 헤더를 통해 알립니다.
패치 형식 발견: 서버는 반드시 JSON Patch 지원을
알려야 합니다 [RFC6902]. 다음을 통해 알립니다:
Accept-Patch 헤더: Accept-Patch: application/json-patch+json.
선택적 메서드: 서버는 PUT 또는 다른 패치 형식을 선택적으로 지원할 수 있습니다. 지원한다면 반드시 Allow 및 Accept-Patch 헤더에 각각 포함해야 합니다.
[!IMPORTANT] 클라이언트는 가급적 PUT 또는 특정 패치 형식의 지원을 가정하지 않는 것이 좋습니다. 리소스 헤더에 명시된 경우는 예외이며, 반드시 405 Method Not Allowed 또는 415 Unsupported Media Type 응답을 적절하게 처리해야 합니다.
메타데이터 관리
메타데이터는 리소스에 연결된 다음 대상과 상호작용하여 관리합니다: 링크 집합
리소스 URI. 서버는 반드시 ETag 헤더를 포함해야 하며,
다음에 정의된 헤더입니다: [RFC9110]. GET 및 HEAD 요청에 대한 응답에 포함합니다.
부분 갱신(PATCH): 메타데이터 관리의 주요 메커니즘입니다. 서버는 반드시
application/json-patch+json을 사용하는 PATCH를 지원해야 합니다.
교체(PUT): Allow 헤더에 명시되어 있으면 클라이언트는 선택적으로 전체 링크 집합을 교체할 수 있습니다. 서버가 PUT을 지원하지 않으면 반드시 405 Method Not Allowed로 요청을 거부해야 합니다.
제한: 서버는 특정 링크의 수정을 선택적으로 제한할 수 있습니다.
(up 또는 items 등) 시스템 무결성을 유지하기 위한 제한입니다. 서버는 반드시 클라이언트가 수정할 권한이 없는 링크를 보존해야 합니다.
패치를 적용할 때 해당됩니다. 클라이언트가 up 링크를 수정하도록 허용하는 서버는 그에 따라
리소스가 다음 간에 이동하는 것을 허용합니다: 컨테이너. 이러한 서버는 반드시
MoveResource 기능을 다음에 설명해야 합니다: 저장소 설명. 이는
다음에 설명되어 있으며, 6.1.3
저장소 기능, 그에 따른 요구 사항은 다음에 정의되어 있습니다: 9.6 리소스 이동
(컨테이너 간).
수명 주기: 메타데이터의 수명 주기는 설명하는 리소스에 종속됩니다. 리소스를 삭제하면 반드시 연결된 다음 대상이 자동으로 제거되어야 합니다: 링크 집합 리소스 메타데이터.
리소스 생성 작업은 새로운 제공 리소스를 기존 컨테이너에 추가합니다. 이 작업은 다음의 생성과 데이터 리소스, 하위 컨테이너의 생성을 모두 처리합니다.
입력:
동작:
가능한 응답:
새 리소스는 대상에 POST를 사용하여 생성합니다: 컨테이너 URI. 서버가
최종 식별자를 할당합니다. 클라이언트는 새 리소스의 초기 사용자 관리 메타데이터를 선택적으로
제공할 수 있으며, POST 요청에 하나 이상의 Link 헤더를 포함하여 다음의 구문을 따릅니다:
웹 링크, 다음에 정의되어 있습니다 [RFC8288]. 서버 관리 메타데이터는 반드시
생성 시 서버가 자동으로 생성해야 하며, 절대로
클라이언트가 제공한 링크로 덮어써서는 안 됩니다.
성공하면 서버는 반드시 201 상태 코드를 반환하고, 새 URI를
Location 헤더에 포함해야 합니다. 서버는 반드시 Link 헤더를 포함해야 하며,
주요 서버 관리 메타데이터에 대한 헤더로, 부모를 가리키는 링크를 포함합니다: 컨테이너
(rel="up"). 생성된 리소스 전용의 다음을 가리키는 링크도 포함합니다: 링크 집합 리소스
(rel="linkset"; type="application/linkset+json"). 추가 링크는 가급적
rel="type"을 포함하는 것이 좋습니다(다음을 나타냅니다:
https://www.w3.org/ns/lws#Container 또는
https://www.w3.org/ns/lws#DataResource). 본문은 선택적으로 비어 있거나
리소스의 최소 표현을 포함할 수 있습니다. 모든 메타데이터 생성과 연결은 반드시 리소스 생성과 원자적으로 수행되어 일관성을 유지해야 합니다.
POST(컨테이너 URI 대상) – 서버가 할당한 이름으로 생성: POST를 사용하여 기존의 다음 내부에 새 리소스를 추가합니다: 컨테이너. 서버가 식별자를 리소스에 할당합니다. 클라이언트는 생성할 리소스의 유형을 다음과 같이 나타냅니다:
rel="type"이 있는 Link 헤더를 포함해야 하며,
Container 유형을 가리켜야 합니다:
Link: <https://www.w3.org/ns/lws#Container>; rel="type".
Content-Type 헤더와 함께 요청 본문에 리소스 콘텐츠를 포함합니다.예시(POST로 새 데이터 리소스 생성):
POST /alice/notes/ HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Type: text/plain
Content-Length: 47
우유
달걀
빵
버터
사과
오렌지 주스
이 예시에서 클라이언트는 다음에 POST 요청을 보냅니다: 컨테이너 /alice/notes/.
text/plain 콘텐츠(식료품 목록)를 제공합니다. /alice/notes/가 존재하고
클라이언트가 인가되었다면, 서버는 새로운 데이터 리소스를 생성하여 다음의 컨테이너 멤버십에 추가합니다.
예시(POST에 대한 응답 — 데이터 리소스):
HTTP/1.1 201 Created
Location: /alice/notes/shoppinglist.txt
Content-Type: text/plain; charset=UTF-8
Link: </alice/notes/shoppinglist.txt.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/notes/>; rel="up"
Link: <https://www.w3.org/ns/lws#DataResource>; rel="type"
Content-Length: 0
예제(존재하지 않는 컨테이너에 POST):
POST /alice/notes/ HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Type: text/plain
Content-Length: 4
test
만약 /alice/notes/ 가 존재하지 않으면 서버는 대상을 찾을 수 없음 결과를 보고한다:
HTTP/1.1 404 Not Found
참고: 리소스 생성 연산은 하나의 기존 컨테이너에 리소스를 추가한다. 포함 관계 는 URI 경로 구조가 아니라 메타데이터로 표현되므로( 7. 논리적 리소스 구성) 이 명세는 한 번의 연산으로 여러 수준의 컨테이너 를 생성하는 것을 정의하지 않는다. 클라이언트는 각 컨테이너 를 개별 리소스 생성 연산으로 생성한다.
생성: 컨테이너: 새로운 컨테이너를 생성하려면 클라이언트는 기존
부모에 POST를 보냅니다: 컨테이너, Link 헤더로
Container 유형을 나타냅니다. 예를 들면 다음과 같습니다:
POST /alice/ HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Length: 0
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
예시(POST에 대한 응답 — 컨테이너):
HTTP/1.1 201 Created
Location: /alice/notes/
Link: </alice/notes/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
Content-Length: 0
이 요청은 새로운 컨테이너를 /alice/notes/에 생성하며, 서버가 생성한
메타데이터에는 rel="type"의 값으로 https://www.w3.org/ns/lws#Container가 포함됩니다.
생성에 대한 추가 참고 사항(HTTP 바인딩):
Link 헤더를 포함하는 것이 좋습니다:
rel="https://www.w3.org/ns/lws#storage", 401 응답에 포함하여
하드코딩된 URI 없이 클라이언트를 안내합니다.
메타데이터 관리 및 조회(생성 관련):
메타데이터는 주로 읽기 작업을 통해 조회하지만, 생성 중에 만들어집니다. 클라이언트는
생성 직후 새 리소스 URI에 GET 또는 HEAD를 사용하여 이를 조회할 수 있습니다. 클라이언트는
Prefer 헤더를 사용하여 특정 메타데이터 링크(관계 유형을 통해) 및
속성의 포함을 요청할 수 있습니다.
기존 리소스의 표현 또는 다음의 목록을 조회합니다: 컨테이너.
리소스 읽기 작업은 HTTP GET 요청으로 리소스 표현을 요청합니다(헤더만 요청하려면
HEAD를 사용합니다). 동작은 대상 URL이 다음인지에 따라 다릅니다: 컨테이너 또는 컨테이너가 아닌 리소스(데이터 리소스). 서버는 반드시
메타데이터를 통해 리소스 유형을 구분해야 합니다. 모든 응답은 반드시
8.1절에 정의된 메타데이터와 통합되어야 하며, 다음과 같은 주요 관계의 Link 헤더를 포함합니다:
rel="linkset", rel="up" 및 rel="type". 서버는 반드시 읽기 중에 리소스 상태와 메타데이터 간의 원자성을 보장해야 합니다.
GET(컨테이너가 아닌 리소스) – 리소스 콘텐츠 조회:
전체 콘텐츠를 얻으려면 리소스 URI에 GET을 보냅니다(인가된 경우). 200 OK로 응답하며, 본문에는
데이터를 포함하고 Content-Type은 저장된 미디어 유형과 일치해야 합니다. 서버는 반드시
다음에 따른 범위 요청을 지원해야 하며, [RFC7233], 부분
조회를 지원합니다. 응답은 반드시 다음에 정의된 ETag 헤더를 포함해야 합니다:
[RFC9110].
예시(GET으로 파일 조회):
GET /alice/notes/shoppinglist.txt HTTP/1.1
Authorization: Bearer <token>
Accept: text/plain
이 요청은 /alice/notes/shoppinglist.txt의 콘텐츠를 요청하며, 클라이언트가
텍스트 형식을 원함을 나타냅니다. 리소스가 존재하고 텍스트이며 클라이언트에게 접근 권한이 있다고 가정합니다:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 34
ETag: "abc123456"
Link: </alice/notes/shoppinglist.txt.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/notes/>; rel="up"
Link: <https://www.w3.org/ns/lws#DataResource>; rel="type"
우유
치즈
빵
과카몰레
탄산음료
초콜릿 바
해시
달걀
서버는 텍스트 콘텐츠를 반환했습니다(Content-Length에 표시된 대로 총 34바이트).
콘텐츠는 파일에 저장된 데이터와 정확히 같습니다. ETag: "abc123456"은 검증자로,
캐싱과 조건부 요청에 사용됩니다. 응답은 메타데이터 발견을 위한 Link 헤더를
포함하며, up 및 type과 같은 필수 필드를 포함합니다.
GET(컨테이너 리소스) – 목록 조회: 컨테이너의 내용:
대상 URI가 다음에 해당하면, 컨테이너(메타데이터 유형으로 결정), GET 요청은
다음의 목록을 반환합니다: 컨테이너의 멤버. 다음을 포함하는 응답은
Content-Type: application/lws+json, 컨테이너
표현이며, 다음에 정의되어 있습니다: 컨테이너
표현 절. 이 표현의 목록에는 각 멤버의 메타데이터가 포함됩니다:
리소스 식별자(필수), 유형(필수), 미디어 유형
(DataResources의 경우 필수), 크기(권장) 및
수정 타임스탬프(권장).
예시(GET으로 컨테이너 조회): 다음이 존재하고, 컨테이너, 클라이언트에게 접근 권한이 있다고 가정합니다. 탭은 동일한 요청을 서로 다른 미디어 유형으로 보여줍니다. 다음을 참조하세요: 12.1.2 콘텐츠 협상.
요청:
GET /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
Accept: application/lws+json
응답 헤더:
HTTP/1.1 200 OK
Content-Type: application/lws+json
ETag: "container-etag-789"
Link: </alice/notes/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
응답 본문:
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/notes/",
"type": "Container",
"totalItems": 2,
"items": [
{
"type": "DataResource",
"id": "/alice/notes/shoppinglist.txt",
"format": "text/plain",
"size": 47,
"modified": "2025-11-24T12:00:00Z"
},
{
"type": ["DataResource", "http://example.org/customType"],
"id": "/alice/notes/todo.json",
"format": "application/json",
"size": 2048,
"modified": "2025-11-24T13:00:00Z"
}
]
}
요청:
GET /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
Accept: text/turtle; profile="https://www.w3.org/ns/lws/v1"
응답 헤더:
HTTP/1.1 200 OK
Content-Type: text/turtle; profile="https://www.w3.org/ns/lws/v1"
ETag: "container-etag-789"
Link: </alice/notes/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
응답 본문:
@prefix lws: <https://www.w3.org/ns/lws#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix schema: <http://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<> a lws:Container ;
lws:totalItems 2 ;
lws:items <shoppinglist.txt>, <todo.json> .
<shoppinglist.txt> a lws:DataResource ;
dcterms:format "text/plain" ;
schema:size "47"^^xsd:long ;
dcterms:modified "2025-11-24T12:00:00Z"^^xsd:dateTime .
<todo.json> a lws:DataResource, <http://example.org/customType> ;
dcterms:format "application/json" ;
schema:size "2048"^^xsd:long ;
dcterms:modified "2025-11-24T13:00:00Z"^^xsd:dateTime .
이 예시에서 /alice/notes/는 컨테이너입니다. 응답은 JSON-LD와
LWS 맥락을 사용하여 필수 메타데이터와 함께 멤버를 나열합니다. 각 항목에는 type,
id, format, size 및 modified 타임스탬프가 평면
속성으로 포함됩니다.
모든 경우에 서버는 반드시 다음 메타데이터를 응답
헤더에 포함해야 합니다: 다음에 정의된 ETag 헤더, [RFC9110], 그리고 Link 헤더,
rel="linkset", rel="up" 및 rel="type"이 있으며, 다음임을 나타냅니다:
컨테이너.
HEAD(모든 리소스 또는 컨테이너) – 헤더/메타데이터만:
LWS 서버는 반드시 HEAD를 지원해야 합니다 [RFC9110]. 다음 모두에 적용됩니다:
컨테이너 및 컨테이너가 아닌 리소스. GET과 동일한 헤더를 반환하며,
(Content-Type, 메타데이터용 Link 포함) 본문은 반환하지 않습니다. 이를 통해
콘텐츠를 전송하지 않고 메타데이터를 조회할 수 있습니다.
캐싱 및 조건부 요청: LWS는 HTTP 캐싱 의미론을 활용합니다. 서버는 가급적 다음에 정의된 조건부 요청을
지원하는 것이 좋습니다: [RFC9110],
엔터티 태그(ETags) 및 날짜 기반 검증자와 같은 메커니즘을 포함합니다(예:
If-Modified-Since 헤더). 리소스 또는 컨테이너 목록이
변경되지 않았다면 304 Not Modified로 응답하여 불필요한 전송을 피합니다.
발견 가능성 및 인가: 발견 가능성을 높이기 위해,
401 Unauthorized 응답에는 WWW-Authenticate 헤더와
resource_metadata 매개변수가 포함되며, 이는 다음에 설명되어 있습니다: 인가 서버 발견. 이를 통해 클라이언트는
하드코딩된 URI 없이 인가 서버를 찾을 수 있습니다.
전체 교체 또는 부분 패치를 통해 기존 [제공 리소스]의 상태를 수정합니다.
리소스 갱신은
기존의 다음 콘텐츠를 수정합니다: 제공
리소스. PUT 요청(전체 리소스 교체) 또는 PATCH 요청(
부분 수정 적용)을 사용합니다. 클라이언트는 이러한 작업을 수행하려면 리소스 URL에 대한 쓰기 접근 권한을
가져야 합니다.
참고: 이 절은 리소스의 주요 콘텐츠 갱신을 설명합니다. 메타데이터를 갱신하려면 다음 절을 참조하세요:
9.3.2.
LWS 서버는 반드시 리소스 URI에 대한 PUT 및 PATCH 요청을
리소스 콘텐츠만 수정하는 것으로 처리해야 하며, 기본적으로 연결된 다음에는 영향을 주지 않습니다: 링크 집합 리소스. 선택적으로
콘텐츠와
메타데이터를 하나의 원자적 작업으로 모두 갱신하려면, 클라이언트는 Link를 선택적으로
리소스 URI에 대한 PUT/PATCH 요청의 헤더에 포함하고 선호 사항을 지정할 수 있습니다:
'Prefer: set-linkset'(다음에 정의되어 있습니다: [RFC7240]). 이 경우
서버는 반드시 제공된 Link 헤더를 교체로 해석하거나,
(PUT의 경우) 링크 집합에 대한 부분 갱신으로 해석해야 하며(PATCH의 경우), 콘텐츠 변경도 함께 적용합니다.
이 동작은 서버에 선택 사항이지만, 지원한다면 반드시 Prefer 헤더를
통해 명시적으로 호출해야 하며,
의도하지 않은 메타데이터 덮어쓰기를 방지하기 위한 것입니다. 결합 갱신을 지원하지 않는 서버는 반드시 해당 선호 사항을 무시하거나
501 Not Implemented로 응답해야 합니다.
PUT(전체 리소스 교체) – 리소스 URI에 PUT을 보내며, 새로운 전체 콘텐츠를
본문에 포함하고 일치하는 Content-Type을 사용합니다(일반적으로 기존 유형과 일치). PUT은
기존 리소스에 대해 멱등적입니다. 클라이언트는 가급적 다음에 정의된 조건부 요청을 사용하는 것이 좋습니다:
[RFC9110], 동시에 발생한 변경 사항을 덮어쓰지 않기 위한 것입니다.
예시(PUT으로 리소스 갱신):
PUT /alice/personalinfo.json HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json
If-Match: "abc123456"
{
"name": "Alice",
"age": 30,
"city": "New London",
"state": "Connecticut"
}
이 예시에서 클라이언트는 다음 위치의 기존 JSON 리소스를 갱신합니다:
/alice/personalinfo.json. 엔터티 태그가 있는 If-Match 헤더를 포함하며,
(ETag) 이전 GET 또는 HEAD 요청에서 받은 태그입니다. 서버는 이 태그를
현재 엔터티 태그(ETag)와 비교합니다. 일치하면 콘텐츠를
제공된 JSON으로 교체합니다. 일치하지 않으면 서버는 갱신을 거부합니다(그동안 다른 사람이
리소스를 변경했기 때문입니다).
성공 응답: 갱신이 성공하면 서버는 200 OK로 응답하고
갱신된 표현 또는 확인 정보(새 콘텐츠나 그 일부 등)를 포함할 수 있습니다.
또는 서버는 204 No Content로 응답하여 본문 없이 성공을 나타낼 수 있습니다.
(추가 정보를 전달할 필요가 없을 때 특히 일반적입니다). 예를 들면 다음과 같습니다:
HTTP/1.1 204 No Content
If-Match 사전 조건이 일치하지 않으면
(동시 수정), 서버는 412 Precondition Failed로 응답합니다.
리소스가 존재하지 않으면 갱신 목적의 PUT은 404 Not Found를 발생시킵니다.
클라이언트가 인가되지 않았으면 403 Forbidden(유효한 자격 증명이
제공되지 않았으면 401 Unauthorized)입니다. 요청 페이로드가 유효하지 않으면 400 Bad Request입니다.PATCH(부분 갱신) – HTTP PATCH 메서드는 [RFC5789]
클라이언트가 새로운 전체 콘텐츠를 보내는 대신 리소스의 부분 수정을 지정할 수 있도록
합니다. 큰 리소스에서 작은 부분만 변경되어 전체 콘텐츠를 보내는 것이 비효율적인
경우나 특정 변경 사항을 적용하려는 동시 편집에 유용합니다. LWS
서버는 반드시 최소한 JSON Patch를 지원해야 합니다.
(application/json-patch+json) 다음에 정의되어 있습니다: [RFC6902].
리소스 메타데이터 갱신(링크 집합에 대한 HTTP PUT / PATCH) 리소스의 메타데이터는 해당하는 다음을 수정하여 갱신합니다: 링크 집합 리소스, rel="linkset"이 있는 Link 헤더를 통해 발견합니다. 전체 교체(PUT): 다음에 PUT 요청을 보내며, 링크 집합 리소스 URI, 완전한 링크 집합 문서를 본문에 포함하여 리소스의 모든 메타데이터를 교체합니다. 부분 갱신(PATCH): 다음에 PATCH 요청을 보내며, 링크 집합 리소스 URI, 특정 링크를 추가하거나 제거하거나 수정합니다.
메타데이터에 대한 조건부 요청
여러 주체가 리소스의 메타데이터를 수정할 수 있으므로, 동시에 발생하는 덮어쓰기를 방지하는 것이
중요합니다. 서버와 클라이언트는 가급적 다음에 정의된 조건부 요청을 사용하는 것이 좋습니다:
[RFC9110], 다음에 대한 PUT 및 PATCH 작업에 사용합니다: 링크 집합 리소스. 서버가 사전 조건을 충족하지 않는 조건부
요청을 받으면, 반드시 다음으로 요청을 거부해야 합니다:
412 Precondition Failed 상태 코드.
예시(PUT으로 링크 집합 교체):
클라이언트가 먼저 링크 집합을 가져옵니다.
GET /alice/personalinfo.json.meta HTTP/1.1
Authorization: Bearer <token>
Accept: application/linkset+json
HTTP/1.1 200 OK
Content-Type: application/linkset+json
{
"linkset": [
{
"anchor": "/alice/personalinfo.json",
"describedby": [ { "href": "/schemas/personal-info.json" } ]
}
]
}
클라이언트는 이제 라이선스를 추가하려고 합니다. 새롭고 완전한 링크 집합 문서를 구성하여 PUT 요청을 보냅니다.
PUT /alice/personalinfo.json.meta HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/linkset+json
{
"linkset": [
{
"anchor": "/alice/personalinfo.json",
"describedby": [ { "href": "/schemas/personal-info.json" } ],
"license": [ { "href": "https://creativecommons.org/licenses/by/4.0/" } ]
}
]
}
성공하면 서버는 성공 응답을 보냅니다.
HTTP/1.1 204 No Content
갱신 규칙 요약 리소스의 콘텐츠만 변경하려면 → 리소스 자체에 PUT/PATCH를 보냅니다. 리소스의 링크(메타데이터)만 변경하려면 → 리소스에 연결된 다음에 PUT/PATCH를 보냅니다: 링크 집합 리소스. 콘텐츠와 링크를 모두 변경하려면 → 리소스 자체에 PUT/PATCH를 보내며, 적절한 Link 헤더와 'Prefer: set-linkset'을 모두 포함합니다. 둘 다 설정하는 동작은 기본적으로 비활성화되어 있습니다.
리소스와 연결된 메타데이터를 영구적으로 제거합니다.
리소스 삭제 작업은 위의 추상 작업에 정의된 대로 HTTP DELETE 메서드를 사용하여 구현합니다. 이 절은 입력, 동작 및 응답의 HTTP 바인딩을 명시합니다.
DELETE 요청은 제거할 리소스 또는 다음의 URI를 대상으로 합니다: 컨테이너. 클라이언트는 선택적으로
ETag가 있는 If-Match 헤더를 포함하여 동시성
검사를 수행할 수 있습니다.
삭제 및 포함 관계:
리소스가 삭제되면 서버는 반드시 해당 리소스를 원자적으로 제거해야 합니다.
부모 컨테이너의 items 목록에서 제거합니다. 부모 컨테이너의
totalItems 개수는 가급적 그에 맞게 갱신하는 것이 좋습니다.
컨테이너가 아닌 리소스의 경우, 서버는 리소스 콘텐츠와 연결된 메타데이터(링크 집합 리소스) 및 포함 관계 참조를 부모에서 제거합니다: 컨테이너.
다음의 경우, 컨테이너 리소스, 서버는 기본적으로 비재귀적
삭제를 수행합니다. 다음이 컨테이너 비어 있지 않고 재귀가 요청되지 않았다면 서버는
반드시 409 Conflict로 요청을 거부해야 합니다. 서버는 선택적으로
다음 내부의 모든 리소스에 대한 재귀적 삭제를 지원할 수 있습니다: 컨테이너, 삭제
대상 컨테이너입니다. 클라이언트는 반드시 Depth: infinity 헤더를 사용하여
재귀적 삭제를 요청해야 하며, 이는 다음에 정의되어 있습니다: [RFC4918].
성공하면 서버는 반드시 204 No Content로 응답해야 합니다. 서버는 가급적 다음에 정의된 조건부 요청을 지원하는 것이 좋습니다: [RFC9110].
클라이언트에게 인가가 없으면 서버는 반드시 403 Forbidden을 반환해야 합니다( 클라이언트의 신원은 알려져 있지만 권한이 부족한 경우). 또는 401 Unauthorized를 반환해야 합니다(유효한 인증이 제공되지 않은 경우). 리소스의 존재를 공개하는 것이 보안 위험을 초래하면, 서버는 선택적으로 대신 404 Not Found를 반환할 수 있습니다.
예시(DELETE로 컨테이너가 아닌 리소스 삭제):
DELETE /alice/notes/shoppinglist.txt HTTP/1.1
Authorization: Bearer <token>
If-Match: "abc123456"
ETag가 일치하고 클라이언트가 인가되었다고 가정하면, 서버는 리소스와 메타데이터를 삭제하고,
부모 컨테이너 /alice/notes/에서 이를 원자적으로 제거합니다:
HTTP/1.1 204 No Content
예시(재귀 없이 DELETE로 비어 있지 않은 컨테이너 삭제):
DELETE /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
/alice/notes/에 리소스가 포함되어 있다고 가정하면, 서버는 삭제를 거부합니다:
HTTP/1.1 409 Conflict
Content-Type: text/plain
삭제할 수 없습니다: 컨테이너 /alice/notes/ - 컨테이너가 비어 있지 않습니다.
예시(지원되는 경우, 재귀를 사용하여 DELETE로 컨테이너 삭제):
DELETE /alice/notes/ HTTP/1.1
Authorization: Bearer <token>
Depth: infinity
서버가 재귀를 지원하고 클라이언트에게 모든 콘텐츠에 대한 권한이 있다고 가정하면, 서버는 컨테이너와 하위 항목을 원자적으로 삭제합니다:
HTTP/1.1 204 No Content
이 명세는 다음을 이동하기 위한 전용 HTTP 메서드를 정의하지 않습니다: 제공 리소스,
한 컨테이너에서 다른 컨테이너로 이동하는 것으로, MOVE 메서드와 다릅니다.
WebDAV가 정의하는 메서드입니다 [RFC4918].
대신 이동은 다음의 갱신으로 표현합니다: 포함 관계 메타데이터, 해당 리소스의 메타데이터입니다.
클라이언트는 리소스의 다음에서 up 링크를 변경합니다: 링크 집합 리소스,
목적지를 가리키도록 변경하며, 목적지는 컨테이너입니다. 다음을 사용합니다: 갱신
리소스 작업. 서버는 해당 변경으로부터 실제 이동을 도출합니다.
서버 지원:
up 링크는 서버가 관리하며, 서버는 클라이언트가 이를
수정하도록 허용할 의무가 없습니다. 이는 다음에 설명되어 있습니다: 9.1
리소스
메타데이터. 클라이언트가 리소스의 up 링크를 수정하도록 허용하여,
그에 따라 다음 간 리소스 이동을 허용하는 서버는, 컨테이너, 반드시 다음을 설명해야 합니다:
MoveResource 기능, 다음에 설명합니다: 저장소
설명. 이는 다음에 설명되어 있으며, 6.1.3 저장소 기능, 또한 반드시
이 절의 요구 사항을 구현해야 합니다. 허용하지 않는 서버는 반드시
409 Conflict와 같은 적절한 4xx 상태 코드로 요청을 거부해야 하며, 절대로
안 됩니다, MoveResource 기능을 설명해서는 안 됩니다.
이동 요청:
클라이언트는 리소스의 다음에 부분 갱신을 적용하여 리소스를 이동합니다: 링크 집합 리소스,
up 링크의 대상을 목적지의 식별자로 교체하며, 목적지는 컨테이너입니다. 다음에서 요구하는 대로, 9.1 리소스 메타데이터, 서버는 반드시
application/json-patch+json을 지원해야 합니다 [RFC6902]. 이 갱신에 대한 지원입니다. 다음의
모든
수정과 마찬가지로, 링크 집합 리소스, 클라이언트는 가급적
현재 ETag를 담은 If-Match 헤더 필드를 포함하여 요청을 조건부로 만드는 것이 좋습니다.
해당 ETag의 대상은 링크 집합 리소스이며, 이는 다음에 설명되어 있습니다: 9.4 리소스 갱신. 서버는
선택적으로 조건부 요청을 요구하고 무조건부 요청을 428
Precondition Required로 거부할 수 있습니다.
목적지는 반드시 기존의 컨테이너여야 합니다. 서버는 반드시 up 링크의
대상을 다음 이외의 것으로 설정하려는 패치를 거부해야 합니다:
기존의 컨테이너. 409
Conflict와 같은 적절한 4xx 상태 코드로 거부합니다. 서버는 마찬가지로 반드시 리소스에
up 링크가 남지 않게 하는 패치를 거부해야 합니다. 모든 루트가 아닌 리소스에는 다음에서 도달할 수 있어야 하기 때문입니다: 저장소 루트.
이 명세는 목적지를 다음으로 제한하지 않습니다: 저장소, 이동할 리소스를 보유한 저장소입니다. 그러나 서버는 리소스를 다음으로 이동하는 것을 지원할 의무가 없습니다: 컨테이너, 다른 저장소에 있는 컨테이너이며, 선택적으로 이러한 패치를 적절한 4xx 상태 코드로 거부할 수 있습니다. 예를 들면 409 Conflict입니다. 이러한 패치를 수락하는 서버에는 이 절의 다른 요구 사항도 계속 적용되며, 변경을 원자적으로 적용해야 한다는 요구 사항도 포함됩니다.
up 링크의 대상을 교체하는 패치는
linkset 배열에서 리소스의 링크 맥락이 차지하는 위치와
up 배열에서 대상 객체가 차지하는 위치에 의존합니다. 클라이언트는 이러한
위치를 다음을 읽어 파악하며, 링크 집합 리소스, 조건부 요청은
패치를 적용할 때도 해당 위치가 유효하도록 보장합니다. 클라이언트는 추가로 변경을 보호할 수 있으며,
test 작업을 사용합니다. 예를 들어 링크 맥락의 anchor 또는
up 링크의 현재 대상을 검사합니다. 다음의 전체 교체와 달리, 링크 집합 리소스, 이러한 패치는 다른 모든 링크를
그대로 유지하므로, 클라이언트가 변경하지 않는 링크를 다시 명시할 필요가 없습니다.
다음에 미치는 영향: 포함 관계:
갱신에 성공하면 서버는 반드시 해당 리소스를 원자적으로 제거해야 합니다.
items 목록에서 제거하며, 이전 부모의 목록입니다: 컨테이너. 그리고 다음에 추가해야 합니다:
items 목록, 목적지의 목록입니다: 컨테이너. totalItems 개수는
두 대상 모두에서, 컨테이너, 가급적 그에 맞게 갱신하는 것이
좋으며, 두 대상의 ETag는 컨테이너, 반드시 갱신하여
변경을 반영해야 합니다.
이동한 리소스의 콘텐츠와 사용자 정의 메타데이터는 이동의 영향을 받지 않습니다. 서버는 반드시 다음의 링크를 보존해야 합니다: 링크 집합 리소스,
클라이언트가 수정할 권한이 없는 링크이며, linkset과 같은 서버 관리 링크
및 type을 포함합니다. 보조 리소스의 수명이 이동한 리소스에 종속되어 있다면,
이동 후에도 계속 해당 리소스에 종속됩니다.
서버는 반드시 다음의 무결성을 위반하는 갱신을 거부해야 합니다: 포함 관계, 해당 무결성의 요구 사항은 다음에 있습니다: 7. 논리적 리소스 구성. 409 Conflict와 같은 적절한 4xx 상태 코드로 거부합니다. 특히 컨테이너는 절대로 자기 자신 내부로 이동하거나 자신의 하위 항목 내부로 이동해서는 안 됩니다. 다음에 순환이 생기기 때문입니다: 포함 관계 계층.
이동한 리소스의 식별 정보:
리소스의 URI는 다음에서의 위치와 독립적이며, 포함 관계 계층,
이는 다음에 설명되어 있습니다: 7.
논리적 리소스 구성. 따라서 서버는 가급적 보존하는 것이 좋습니다.
이동한 리소스의 식별자를 보존하여 기존 링크가 계속 유효하도록 하며, 가급적 그 여부를
preservesIdentifier로 선언하는 것이 좋습니다.
MoveResource 기능의 속성입니다. 해당 속성이 없으면 클라이언트는 절대로 식별자가 보존된다고 가정해서는 안
됩니다.
이동한 리소스의 식별자를 보존하지 않는 서버는 반드시 응답해야 합니다.
200 OK와 함께 갱신된 다음의 표현을 반환합니다: 링크 집합 리소스, 그
anchor는 리소스의 새 식별자여야 하며, 반드시 다음을 포함해야 합니다:
Content-Location 헤더 필드, 다음의 새 식별자를 담는 필드입니다: 링크 집합 리소스 자체. 이러한
서버는 가급적 이전 식별자에 대한 후속 요청에 응답하는 것이 좋습니다.
리소스 및 해당 보조 리소스의 이전 식별자에 대해 301 Moved Permanently로 응답하며,
새 식별자를 가리키는 Location 헤더 필드를 포함합니다.
이동: 컨테이너:
서버는 선택적으로 다음의 up 링크 변경을 지원할 수 있습니다: 컨테이너. 또한 반드시 지원 여부를 나타내야 하며,
Container를 다음의 appliesTo 속성에 포함합니다:
MoveResource 기능. 다음이 이동하면, 컨테이너, 하위 항목도
함께 이동하며, 하위 항목의 식별자에는 이동한 다음의 식별자와 동일한 요구 사항이 적용됩니다: 컨테이너. 다음의 이동은 지원하지만, 데이터 리소스, 다음의 이동은 지원하지 않는 서버는, 컨테이너, 반드시 409
Conflict와 같은 적절한 4xx 상태 코드로 해당 요청을 거부해야 하며, 절대로 Container를
appliesTo 속성에 포함해서는 안 됩니다.
인가:
서버는 절대로 up 링크에 변경을 적용해서는 안 됩니다. 단,
요청하는 에이전트가
다음 두 작업을 모두 수행하도록 인가된 경우는 예외입니다: 리소스 삭제 작업을 이동할 리소스에 수행하는 것과,
리소스 생성 작업을 목적지에서 수행하는 것입니다: 컨테이너.
다음이 이동하면, 컨테이너, 해당 컨테이너만이 부모가 변경되는 리소스입니다. 하위 항목은 서로에 대한 상대적 위치를 유지하며, 서버가 식별자를 보존하는 경우 식별자도 유지합니다. 따라서 에이전트는 목적지에서 각 하위 항목을 다시 생성하도록 인가받을 필요가 없습니다. 그러나 해당 하위 항목에 적용되는 정책은 변경될 수 있습니다. 접근은 흔히 다음 내의 위치를 기준으로 제어되기 때문입니다: 포함 관계 계층. 이러한 이유로, 다음을 이동하는 데 필요한 인가는 컨테이너, 해당 대상을 삭제하는 데 필요한 인가입니다. 재귀적으로 삭제하는 경우이며, 이는 다음에 설명되어 있습니다: 9.5 리소스 삭제. 여기에 목적지에서 생성하는 데 필요한 인가도 있어야 합니다: 컨테이너. 서버는 반드시 해당 인가를 전체 하위 트리에 대해 평가한 후 변경을 적용해야 하며, 절대로 변경을 부분적으로 적용해서는 안 됩니다.
예시(데이터 리소스를 다른 컨테이너로 이동):
이 예시에서 저장소는 다음을 반영하지 않는 불투명한 식별자를 할당합니다: 포함 관계.
이동할 리소스는 https://storage.example/f8c3de3d로 식별되며, 해당 링크 집합 리소스는
https://storage.example/9b1e07a4로, 출발지와 목적지의 컨테이너는
https://storage.example/1d4f8a02 및 https://storage.example/c2a71f60으로
각각 식별됩니다. 클라이언트는
다음을 읽었으며, 링크 집합 리소스, 여기서 up 링크는
첫 번째 링크 맥락의 첫 번째 대상 객체입니다.
PATCH /9b1e07a4 HTTP/1.1
Host: storage.example
Authorization: Bearer <token>
Content-Type: application/json-patch+json
If-Match: "meta-v3"
[
{ "op": "replace", "path": "/linkset/0/up/0/href", "value": "https://storage.example/c2a71f60" }
]
클라이언트가 인가되었고 목적지인 다음이 컨테이너 존재한다고 가정하면, 서버는
리소스를 https://storage.example/1d4f8a02의 items 목록에서 제거하고,
https://storage.example/c2a71f60의 items 목록에 추가하며, 다음을 갱신합니다: 링크 집합 리소스. 이들은 원자적으로 수행됩니다:
HTTP/1.1 204 No Content
ETag: "meta-v4"
Link: <https://storage.example/c2a71f60>; rel="up"
리소스는 계속 https://storage.example/f8c3de3d에서 이용할 수 있으며, 이제 다음의 멤버입니다:
https://storage.example/c2a71f60. 리소스의 식별자는 자신의
위치를
계층 내에서 인코딩한 적이 없으므로, 이동은 다음에서만 관찰할 수 있습니다: 포함 관계 메타데이터.
예시(서버가 새 식별자를 할당하는 이동):
PATCH /alice/notes/shoppinglist.txt.meta HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Type: application/json-patch+json
If-Match: "meta-v3"
[
{ "op": "replace", "path": "/linkset/0/up/0/href", "value": "/alice/archive/" }
]
다음에서 식별자를 도출하는 서버는, 포함 관계 계층, 따라서
"preservesIdentifier": false를 알리며, 식별자를 보존할 수 없습니다. 서버는
결과 식별자를 갱신된 다음에 보고합니다: 링크 집합 리소스:
HTTP/1.1 200 OK
Content-Type: application/linkset+json
Content-Location: /alice/archive/shoppinglist.txt.meta
ETag: "meta-v4"
{
"linkset": [{
"anchor": "/alice/archive/shoppinglist.txt",
"up": [{ "href": "/alice/archive/" }],
"linkset": [{ "href": "/alice/archive/shoppinglist.txt.meta",
"type": "application/linkset+json" }]
}]
}
예시(컨테이너를 자신의 하위 항목 중 하나로 이동):
PATCH /alice/notes/.meta HTTP/1.1
Host: example.com
Authorization: Bearer <token>
Content-Type: application/json-patch+json
If-Match: "meta-v7"
[
{ "op": "replace", "path": "/linkset/0/up/0/href", "value": "/alice/notes/2026/" }
]
서버는 갱신을 거부합니다. 다음에 순환이 생기기 때문입니다: 포함 관계 계층:
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"title": "컨테이너를 자신의 하위 항목 중 하나로 이동할 수 없습니다",
"status": 409,
"detail": "/alice/notes/2026/은 /alice/notes/의 하위 항목입니다"
}
알림:
리소스 이동 지원을 알리고 알림을 내보내는 서버는 가급적 성공한 이동을 하나의 Move 활동으로 설명하는 것이
좋습니다. 이는
다음에 설명되어 있으며, 10.2.3 활동
유형, 별도의 Delete 및 Create 활동 대신 사용합니다.
이 표는 일반적인 LWS 응답(출처: 8절)을 HTTP 상태 코드와 페이로드에 매핑하여 일관성을 유지하며, 다음과 같은 구체적인 상황을 반영합니다: 페이지 나누기, 조건부 요청, 할당량 제약 및 메타데이터 통합:
| LWS 응답 | HTTP 상태 코드 | HTTP 페이로드 |
|---|---|---|
| 성공(읽기 또는 갱신, 반환 내용: 데이터) | 200 OK |
리소스
표현이 응답 본문에 포함됩니다(GET 또는 PUT/PATCH가 콘텐츠를 반환하는 경우).
관련 헤더도 포함합니다(Content-Type, 다음과 같은 메타데이터용 Link:
rel="linkset" 또는 rel="up"). 컨테이너 목록에는
규범적 맥락과 멤버 메타데이터(ID, 유형, 크기, 타임스탬프 등)가 있는 JSON-LD를 포함합니다.
|
| 생성됨(새 리소스) | 201 Created |
일반적으로 응답 본문이 없습니다(또는 새 리소스의 최소 표현이 있습니다).
Location 헤더는 새 리소스의 URI로 설정됩니다. Link 헤더는
서버 관리 메타데이터에 사용됩니다.
|
| 삭제됨(반환할 콘텐츠 없음) | 204 No Content |
응답 본문이 없습니다. 리소스가 삭제되었거나 요청이 성공했으며
추가로 전달할 내용이 없음을 나타냅니다. 서버는 선택적으로 410 Gone을
영구 삭제에 사용할 수 있습니다. |
| 잘못된 요청(유효하지 않은 입력 또는 제약 조건) | 400 Bad Request |
무엇이 잘못되었는지 설명하는 오류 세부 정보입니다. 서버는 가급적 다음에
정의된 표준 형식을 사용하는 것이 좋습니다: [RFC9457],
구조화된 오류 응답에 사용합니다. 예를 들어 "type",
"title", "status", "detail" 및
"instance"와 같은 필드가 있는 JSON 객체입니다.
|
| 알 수 없는 요청자 | 401 Unauthorized |
선택적 오류 세부 정보. 예를 들어 [RFC9457]에서 정의한 형식을 사용할 수 있다.
WWW-Authenticate 헤더는 클라이언트가 인증할 수 있는 방법을 나타낸다.
|
| 허용되지 않음 | 403 Forbidden |
선택적 오류 세부 정보. 예를 들어 [RFC9457]에서 정의한 형식을 사용할 수 있다. |
| 대상을 찾을 수 없음 | 404 Not Found |
선택적 오류 세부 정보. 예를 들어 [RFC9457]에서 정의한 형식을 사용할 수 있다. |
리소스에 접근할 권한이 없는 클라이언트에게 리소스 존재 여부를 공개하고 싶지 않은 서버는 [RFC9110]에 설명된 대로
403 Forbidden 대신 404 Not Found로 응답할 수 있다.
이 절은 클라이언트가 변경 사항을 제때 전달받는 메커니즘을 정의하며, Linked Web Storage 서버가 관리하는 리소스의 변경 사항을 다룹니다. 이 명세는 공통 알림 데이터 모델과 구독 메커니즘을 정의합니다. 구체적인 전달 채널은 별도의 알림 스위트 명세에 설명되어 있습니다.
저장소는 알림을 선택적으로 지원할 수 있습니다. 알림을 지원하는 저장소는 반드시
자신의 다음 리소스의 service 배열에 서비스 객체를 명시해야 합니다: 저장소 설명
리소스. 해당 객체의 type은 NotificationService여야 합니다.
서비스 객체는 반드시 serviceEndpoint 속성을 포함해야 하며, 그
값은
구독 엔드포인트의 URI입니다. 서비스 객체는 또한 반드시 다음을 포함해야 합니다:
subscriptionType 속성. 그 값은 문자열 배열이며, 각 문자열은
지원되는 구독 유형을 식별합니다.
서비스 객체에 추가 속성이 선택적으로 존재할 수 있습니다.
{
"@context": ["https://www.w3.org/ns/lws/v1"],
"id": "https://storage.example/",
"type": "Storage",
"service": [{
"type": "NotificationService",
"serviceEndpoint": "https://notification.example/subscriptions",
"subscriptionType": [
"WebhookSubscription"
]
}]
}
서버는 모든 구독 유형을 지원할 의무가 없습니다.
subscriptionType 배열은 이용할 수 있는 유형을 정확하게 나타냅니다.
알림은 JSON-LD 문서로 표현되며,
Notification 유형을 가집니다. 봉투에는 전달
맥락에 관한 메타데이터가 담기며, 활동 스트림 2.0 [ACTIVITYSTREAMS-CORE]
[ACTIVITYSTREAMS-VOCABULARY]
활동을 감싸며, 이 활동이 이벤트를 설명합니다.
알림 봉투에는 다음 속성이 있습니다:
type — 필수입니다. 값은 반드시
다음 문자열이어야 합니다:
Notification.
storage — 필수입니다. LWS 저장소를 식별하는 URI이며,
알림이 연결된 저장소입니다.
activity — 필수입니다. JSON 객체 또는 JSON
객체의 배열이며,
각 객체는 활동 스트림 2.0 데이터 모델을 준수하고, 발생한 이벤트 또는
이벤트들을 설명합니다. 다음을 참조하세요: 10.2.2
활동 속성.
{
"@context": [
"https://www.w3.org/ns/lws/v1",
"https://www.w3.org/ns/activitystreams"
],
"type": "Notification",
"storage": "https://storage.example/",
"activity": {
"id": "ec4dcc48-6581-4232-81c9-584525a98693",
"type": ["Delete"],
"object": {
"id": "https://storage.example/alice/notes/shopping.txt",
"type": ["DataResource"]
},
"origin": "https://storage.example/alice/notes/",
"published": "2026-03-26T10:30:00Z"
}
}
activity 속성 내의 각 활동 객체가 반드시 준수해야 하는
것은
활동 스트림 2.0 데이터 모델입니다. 또한 반드시 다음 속성을 포함해야 합니다:
id — 필수입니다. 활동을 고유하게 식별하는 문자열입니다.
type — 필수입니다. 문자열 배열이며, 최소한
하나의
활동 스트림 2.0 활동 유형을 포함합니다.
object — 필수입니다. 리소스를 식별하는 JSON 객체이며,
알림이 다루는 리소스입니다. object가 반드시 포함해야 하는
속성은
다음과 같습니다:
id — 필수입니다. 리소스의 URI입니다.
type — 필수입니다. 문자열 배열로서,
최소한
하나의 리소스 유형을 포함합니다. 이 명세에 정의된 값으로는
Container 및 DataResource가 있습니다. 추가 유형 값을
선택적으로 포함할 수 있습니다.
published — 필수입니다. 다음에 부합하는 날짜·시간 값이며,
[RFC3339], 시점을 나타냅니다.
활동이 발생한 시점입니다.
다음 속성은 선택 사항입니다:
actor — 행위를 수행한 에이전트를 식별하는 URI입니다.
target — 컨테이너를 식별하는 URI이며,
리소스가 추가된 컨테이너입니다. Create 및 Move 활동에 사용합니다.
origin — 컨테이너를 식별하는 URI이며,
리소스가 제거된 컨테이너입니다. Delete 및 Move 활동에 사용합니다.
서버는 반드시 다음 활동 스트림 2.0 활동 유형을 지원하여 다음의 변경을 나타내야 합니다: 저장소 리소스 변경:
Create — 컨테이너에 새 리소스가 생성되었습니다.
target 필드는 리소스가 추가된 컨테이너를 나타냅니다.
Update — 기존 리소스의 콘텐츠 또는 메타데이터가 수정되었습니다.
Delete — 리소스가 제거되었습니다. origin 필드는
리소스가 제거된 컨테이너를 나타냅니다.
클라이언트가 컨테이너 간에 리소스를 이동하도록 허용하는 서버는, 다음에 설명된 대로, 9.6 리소스 이동 (컨테이너 간), 반드시 다음 활동 유형을 지원해야 합니다:
Move — 리소스가 한 컨테이너에서 다른 컨테이너로 이동되었습니다.
origin 필드는 리소스가 제거된 컨테이너를 나타내고,
target 필드는 리소스가 추가된 컨테이너를 나타냅니다.
object 필드는 이동 후 존재하는 리소스를 설명합니다.
다른 활동 유형도 선택적으로 지원할 수 있습니다.
서버는 여러 활동을 하나의 알림으로 선택적으로 결합할 수 있으며,
해당 봉투에
활동 객체의 배열을 activity의 값으로 제공하여
수행합니다.
{
"@context": [
"https://www.w3.org/ns/lws/v1",
"https://www.w3.org/ns/activitystreams"
],
"type": "Notification",
"storage": "https://storage.example/",
"activity": [
{
"id": "a1b2c3d4-5678-9abc-def0-1234567890ab",
"type": ["Create"],
"object": {
"id": "https://storage.example/alice/notes/meeting.txt",
"type": ["DataResource"]
},
"target": "https://storage.example/alice/notes/",
"published": "2026-03-26T10:30:00Z"
},
{
"id": "b2c3d4e5-6789-abcd-ef01-234567890abc",
"type": ["Update"],
"object": {
"id": "https://storage.example/alice/profile",
"type": ["DataResource"]
},
"published": "2026-03-26T10:30:01Z"
}
]
}
알림을 받기 위해 구독자는 구독을 생성하며,
요청을 보냅니다.
인증된 POST 요청을 serviceEndpoint로 보내며, 해당 속성은
NotificationService에 속합니다. 요청 본문은 반드시 다음을 준수해야 합니다:
application/lws+json 미디어 유형.
구독 요청은 반드시 다음 필드를 포함해야 합니다:
type — 필수입니다. 구독을 식별하는 문자열이며,
구독 유형을 식별합니다.
값은 반드시 다음에 나열된 유형 중 하나여야 합니다:
subscriptionType,
NotificationService의 배열입니다.
topic — 필수입니다. URI 배열로서,
리소스를
식별하며, 구독에 포함된 리소스입니다.
구독 요청은 추가 필드를 선택적으로 포함할 수 있으며, 이는 특정 구독 유형에서 요구하는 필드입니다.
POST /subscriptions HTTP/2
Host: notification.example
Authorization: Bearer <access-token>
Content-Type: application/lws+json
{
"@context": ["https://www.w3.org/ns/lws/v1"],
"type": "WebhookSubscription",
"topic": [
"https://storage.example/alice/notes/",
"https://storage.example/alice/profile"
],
"inbox": "https://receiver.example/hooks/lws"
}
구독이 컨테이너에 대한 것이면 재귀적입니다. 구독자는 컨테이너 자체와 추이적으로 포함된 모든 리소스에 대한 알림을 받습니다. 해당 컨테이너에 포함된 리소스입니다. 구독이 데이터 리소스에 대한 것이면 해당 개별 리소스에만 적용됩니다.
성공적인 구독 생성 요청에 대한 응답이 반드시 준수해야 하는
것은
application/lws+json 미디어 유형입니다. 또한 반드시 다음
필드를 포함해야 합니다:
type — 필수입니다. 구독 유형과 같은 문자열이며,
해당 유형의 출처는
요청입니다.
subscription — 필수입니다. 다음을 나타내는 URL입니다:
구독.
각 알림 스위트는 반드시
구독 유형 문자열과 연결되어야 합니다.
구독 유형 문자열은 다음의 subscriptionType 배열에 사용됩니다:
NotificationService. 또한 구독 요청의 type 필드에도 사용됩니다.
접근 요청 및 권한 부여는 다음을 위한 메커니즘을 제공합니다: 에이전트가 접근을 요청하도록 하고, 리소스에 대한 요청입니다. 또한 저장소 제어자가 정의된 제약 조건에 따라 접근 권한을 부여하도록 합니다. 접근 요청 및 권한 부여 엔드포인트는 특수한 수신함의 한 유형으로 기능합니다.
에이전트는 다음을 제시하지 않습니다: 접근 권한 부여, 서버에 인가 토큰으로 제시하지 않습니다. 대신 접근 권한 부여는 무엇을 누구에게 인가했는지, 어떤 제약 조건에 따르는지를 기록합니다. 다음이 접근 권한 부여 생성되거나 철회(삭제)되면, 서버는 관련된 기본 접근 정책을 조정하여 변경을 반영할 책임이 있습니다. 다음 간의 상호작용은 접근 권한 부여 엔드포인트와 특정 정책 적용 계층 간의 상호작용이며, 이 명세의 범위에 포함되지 않습니다.
다음의 접근 프로파일은 이 절에 정의되어 있으며, 다음의 개념에 기반합니다: 해당 명세인 개방형 디지털 권리 언어(ODRL), 여기에는 행위, 제약 조건 및 할당 대상자가 포함됩니다. 다른 프로파일은 다른 개념 체계를 사용할 수 있습니다.
저장소는 선택적으로 다음에 대한 지원을 알릴 수 있습니다: 접근 요청 및
접근
권한 부여,
자신의 저장소 설명 리소스에서 알립니다. 이러한 지원을
알리는 경우, 서비스
객체는 다음의 엔드포인트를 설명하며, 접근 요청, 반드시 다음을 가져야 합니다:
type, 그 값은 문자열 AccessRequestService여야 합니다. 또한
다음의 엔드포인트를 설명하는 서비스 객체는 접근 권한 부여, 반드시
다음을 가져야 합니다:
type, 그 값은 문자열 AccessGrantService여야 합니다. 서비스
객체는 가급적 conformsTo 속성을 포함하는 것이 좋으며, 그 값은
컬렉션입니다.
하나 이상의 URI로 구성되며, 다음을 식별합니다: 접근 프로파일, 해당
엔드포인트에서 지원하는 프로파일입니다. 추가 속성을 선택적으로 포함할 수 있습니다:
{
"@context": [
"https://www.w3.org/ns/cid/v1",
"https://www.w3.org/ns/lws/v1"
],
"id": "https://storage.example/",
"type": "Storage",
"service": [
{
"type": "StorageRoot",
"serviceEndpoint": "https://storage.example/root/"
},
{
"type": "AccessRequestService",
"serviceEndpoint": "https://access.example/request/",
"conformsTo": ["https://www.w3.org/ns/lws#AccessProfile"]
},
{
"type": "AccessGrantService",
"serviceEndpoint": "https://access.example/grant/",
"conformsTo": ["https://www.w3.org/ns/lws#AccessProfile"]
}
]
}
접근 요청 또는 접근 권한 부여는 데이터
객체이며, 속성을 표현합니다.
식별과 라우팅에 대한 속성 및 다음을 설명하는 access 객체를 표현합니다:
요청되거나 허용된 작업. 다음을 설명하는 프로파일은 접근 요청,
ODRL 개념을 사용하며 다음에 설명되어 있습니다: 11.3
접근 프로파일.
다음 예시는 접근 요청을 보여줍니다. 이 예시는 다음에 정의된 JSON-LD 직렬화를 사용하여 표시됩니다: 11.4.1 JSON-LD 직렬화.
{
"@context": [
"https://www.w3.org/ns/lws/v1"
],
"type": ["AccessRequest"],
"inbox": "https://id.example/agent/inbox/",
"storage": "https://storage.example/",
"access": [{
"type": ["AccessPolicy"],
"action": ["read", "create"],
"assignee": "https://id.example/agent",
"target": {
"type": "StorageResource",
"value": ["https://storage.example/root/projects/"]
},
"constraint": [
{
"leftOperand": "purpose",
"operator": "eq",
"rightOperand": "https://purpose.example/collaboration"
},
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
}
]
}]
}
다음 예시는 접근 권한 부여를 보여줍니다. 이 예시는 다음에 정의된 JSON-LD 직렬화를 사용하여 표시됩니다: 11.4.1 JSON-LD 직렬화.
{
"@context": [
"https://www.w3.org/ns/lws/v1"
],
"type": ["AccessGrant"],
"storage": "https://storage.example/",
"access": [{
"type": ["AccessPolicy"],
"action": ["read"],
"assignee": "https://id.example/agent",
"target": {
"type": "StorageResource",
"value": ["https://storage.example/root/projects/"]
},
"constraint": [
{
"leftOperand": "purpose",
"operator": "eq",
"rightOperand": "https://purpose.example/collaboration"
},
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
}
]
}]
}
다른 속성이 선택적으로 다음에 존재할 수 있습니다: 접근 요청 및 접근 권한 부여.
type 속성은 문서의 유형을 표현합니다.
type 속성의 값은 반드시 하나 이상의
용어 및
절대 URL 문자열이어야 합니다. 다음의 경우,
접근 요청, 값은 반드시
용어 AccessRequest를 포함해야 합니다.
다음의 경우, 접근 권한 부여, 값은 반드시
다음 용어를 포함해야 합니다:
AccessGrant. 추가 유형 값을 선택적으로 포함할 수 있습니다.
이 속성은 필수입니다.
이 문서는 다음에 대해 아래 유형 값을 정의합니다: 접근 요청 및 접근 권한 부여:
AccessRequest — 다음 주체의 요청입니다: 에이전트, 리소스 접근에 대한 요청입니다.
AccessGrant — 다음 주체의 인가입니다: 저장소
제어자, 다음을
부여합니다: 리소스 접근 권한.
{
"type": ["AccessGrant"]
}
storage 속성은 LWS 저장소를 식별하며, 다음의
접근 요청 또는 접근 권한 부여가
적용되는 범위입니다.
storage 속성의 값은 반드시 URI여야 합니다.
이 속성은 필수입니다.
{
"storage": "https://storage.example/"
}
inbox 속성은 다음과 관련된 알림을 수신하기 위한 URI를 지정합니다:
해당 접근 요청 또는 접근 권한 부여
(다음 참조: 11.6
알림).
inbox 속성의 값은 반드시 URI여야 합니다.
이 속성은 선택 사항입니다.
{
"inbox": "https://id.example/agent/inbox/"
}
access 속성은 하나 이상의 다음 특성을 설명합니다:
접근 프로파일, 서버가 지원하는 프로파일에
기반합니다.
다음에 기반하는 프로파일은
ODRL 정보 모델, 다음에 설명되어 있습니다:
11.3 접근 프로파일.
access 속성의 값은 반드시 하나
이상의
객체로 구성된 컬렉션이어야 합니다.
이 속성은 필수입니다.
이 명세는 다음에 기반하는 접근 프로파일을 정의합니다:
ODRL 정보
모델 및 ODRL
어휘, 다음으로 식별됩니다.
해당 URL은
https://www.w3.org/ns/lws#AccessProfile입니다.
type 속성은 접근 정책의 유형을 표현합니다.
type 속성의 값은 반드시 하나 이상의
용어 및
절대 URL 문자열이어야 하며, 반드시 포함해야 합니다.
용어 AccessPolicy를 포함해야 합니다. 추가 유형 값을 선택적으로 포함할 수 있습니다.
이 속성은 필수입니다.
{
"access": [{
"type": ["AccessPolicy"]
}]
}
action 속성은 다음을 설명합니다: 작업, 다음 주체가 에이전트, 수행하려는
작업(요청의 경우) 또는 수행하도록 인가받은 작업(권한 부여의 경우)입니다.
action 속성의 값은 반드시 하나
이상의
문자열로 구성된 컬렉션이어야 합니다. 각 값은 반드시 다음이 인식하는 작업에 대응해야 합니다:
서버.
다음 값은 반드시 지원해야 합니다: read, modify,
create 및 delete. read,
modify 및 delete 값은 다음에 정의되어 있습니다:
ODRL 어휘.
create 값은 이
명세에 정의되어 있습니다.
이러한 행위 값은 다음과 같이 LWS 작업에 대응합니다:
read — 리소스 또는 메타데이터 조회(HTTP GET, HEAD)modify — 기존 리소스 수정(HTTP PUT, PATCH)create — 컨테이너 내에 새 리소스 생성(HTTP POST)
delete — 기존 리소스 제거(HTTP DELETE)이 속성은 필수입니다.
{
"access": [{
"action": ["read", "create"]
}]
}
assignee 속성은 접근을 요청하는 주체(
요청의 경우) 또는 접근 권한을 부여받는 주체(권한 부여의 경우)를 식별합니다.
assignee 속성의 값은 반드시 URI여야 합니다. 공개
접근 권한은 선택적으로
Agent 클래스를 사용하여 부여할 수 있으며, 다음의 클래스입니다: FOAF
어휘, 다음 URI로 식별됩니다:
http://xmlns.com/foaf/0.1/Agent.
이 속성은 필수입니다.
{
"access": [{
"assignee": "https://id.example/agent"
}]
}
target 속성은 리소스를 식별하며, 다음의
접근 요청 또는 접근 권한 부여가
적용되는 리소스입니다.
target 속성은 반드시 다음
속성을 포함하는 객체여야 합니다:
type — 용어 또는
절대 URL 문자열로서,
해당
대상 일치자의 유형을 식별합니다.
value — 하나 이상의 문자열로 구성된 컬렉션으로,
대상 리소스를 식별하는 데 사용합니다.
이 명세는 다음 type 값을 정의하며,
대상 객체와 함께 사용합니다:
https://www.w3.org/ns/lws#DataResource — 다음과 일치합니다:
lws:DataResource 유형의 리소스.
https://www.w3.org/ns/lws#Container — 다음과 일치합니다:
lws:Container 유형의 리소스.
https://www.w3.org/ns/lws#StorageResource — 다음과 일치합니다:
모든 저장소 리소스.
서버는 추가 유형 값을 선택적으로 지원할 수 있습니다.
이 속성은 선택 사항입니다.
{
"access": [{
"target": {
"type": "StorageResource",
"value": [
"https://storage.example/root/2025",
"https://storage.example/root/2026"
]
}
}]
}
constraint 속성은 요청하거나 부여하는 접근을 제한하거나 한정하는
조건을 정의합니다. 이 속성은 ODRL의
제약 조건 모델을 사용합니다.
constraint 속성의 값이 있는 경우, 그 값은 반드시 하나의
모음이어야 하며,
제약 조건 객체로 구성되어야 합니다. 각 제약 조건 객체는 반드시 다음
속성을 포함해야 합니다:
leftOperand — 제약 대상 피연산자를 식별하는 문자열입니다.
operator — 비교 연산자를 식별하는 문자열입니다.
rightOperand — 비교할 값입니다. 타입은
leftOperand에 따라 달라집니다.
여러 제약 조건 객체가 있는 경우, 모든 제약 조건이 반드시 충족되어야 합니다.
이 프로파일의 지원을 알리는 서버는 반드시 다음
leftOperand 값을 지원해야 합니다:
client — HTTP 요청의 클라이언트 식별자를 나타냅니다
format — 대상 HTTP 리소스의 미디어
타입을 나타냅니다
type — 리소스의 링크 헤더에 표현된 타입
URL을 나타냅니다
purpose — 요청하거나 부여하는 접근의 의도된
용도를 나타냅니다
dateTime — 현재 날짜와 시간을 나타냅니다
이 속성은 선택 사항입니다.
다음 예시는 이러한 제약 조건의 사용법을 보여 줍니다:
다음 예시는 leftOperand 값으로
purpose를 사용하여 요청하거나 부여하는 접근의 의도된
용도를 설명합니다. eq 연산자를
사용하는 경우, rightOperand 값은 목적을 식별하는 URI입니다.
isAnyOf 연산자를 사용하는 경우, rightOperand
값은 목적 URI의 배열이며, 명시된 목적이
나열된 값 중 하나와 일치하면 제약 조건이 충족됩니다:
{
"access": [{
"constraint": [
{
"leftOperand": "purpose",
"operator": "isAnyOf",
"rightOperand": [
"https://purpose.example/collaboration",
"https://purpose.example/research"
]
}
]
}]
}
다음 예시는 leftOperand 값으로
client를 사용하여 접근을
특정 클라이언트 애플리케이션으로 제한합니다. eq 연산자를 사용하는 경우,
rightOperand 값은 클라이언트를 식별하는 URI입니다:
{
"access": [{
"constraint": [
{
"leftOperand": "client",
"operator": "eq",
"rightOperand": "https://app.example/client-id"
}
]
}]
}
다음 예시는 leftOperand 값으로
format을 사용하여
접근을 특정 미디어 타입의 리소스로 제한합니다. eq 연산자를
사용하는 경우, rightOperand 값은
IANA 미디어 타입을 식별하는 문자열입니다.
isAnyOf 연산자를 사용하는 경우, rightOperand 값은
미디어 타입 문자열의 배열이며, 리소스의
미디어 타입이 나열된 값 중 하나와 일치하면 접근이 허용됩니다:
{
"access": [{
"constraint": [
{
"leftOperand": "format",
"operator": "isAnyOf",
"rightOperand": ["image/jpeg", "image/png"]
}
]
}]
}
모든 저장소 리소스에는 타입 값을 나타내는 링크 헤더가 포함됩니다:
Link: <https://www.w3.org/ns/lws#DataResource>; rel="type"
Link: <https://type.example/Playlist>; rel="type"
다음 예시는 leftOperand 값으로
type을 사용하여
HTTP Link 헤더에 공지된 리소스 타입을 기준으로 접근을 제한합니다.
eq 연산자를 사용하는 경우, rightOperand 값은
리소스 타입을 식별하는 URI입니다. isAnyOf 연산자를
사용하는 경우, rightOperand 값은 타입 URI의 배열이며, 리소스의 타입이
나열된 값 중 하나와 일치하면 접근이 허용됩니다:
{
"access": [{
"constraint": [
{
"leftOperand": "type",
"operator": "isAnyOf",
"rightOperand": [
"https://type.example/Playlist",
"https://type.example/Song"
]
}
]
}]
}
다음 예시는 leftOperand 값으로
dateTime을 사용하여 접근을
특정 시간 구간으로 제한합니다. gteq 연산자는 구간의 시작을
정의하고, lteq 연산자는 끝을 정의합니다.
시간 제약 조건의 rightOperand 값은
XML
스키마 dateTime 문자열입니다:
{
"access": [{
"constraint": [
{
"leftOperand": "dateTime",
"operator": "gteq",
"rightOperand": "2026-03-09T12:00:00Z"
},
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
}
]
}]
}
여러 제약 조건 객체가 있는 경우, 모든 제약 조건이 반드시 충족되어야 접근이 허용됩니다. 다음 예시는 특정 클라이언트 애플리케이션에서 특정 날짜 이전에 이루어지는 접근으로 제한합니다:
{
"access": [{
"action": ["read"],
"assignee": "https://id.example/agent",
"target": {
"type": "StorageResource",
"value": ["https://storage.example/root/projects/"]
},
"constraint": [
{
"leftOperand": "dateTime",
"operator": "lteq",
"rightOperand": "2026-06-09T10:00:00Z"
},
{
"leftOperand": "client",
"operator": "eq",
"rightOperand": "https://app.example/client-id"
}
]
}]
}
이 절에서 정의하는 데이터 모델은 특정 직렬화 방식과 무관합니다.
이 명세는 접근 요청
및
접근 권한 부여를 위한 JSON-LD 직렬화를 정의합니다. 이 직렬화를 사용하는 문서는 반드시
@context 속성을 포함해야 하며, 그 값은
https://www.w3.org/ns/lws/v1을 포함하는 순서 있는 집합이어야 합니다. 추가 맥락 항목을 선택적으로
더
포함하여 확장 용어를 정의할 수 있습니다.
이 직렬화와 연관된 미디어 타입은 application/lws+json입니다.
{
"@context": [
"https://www.w3.org/ns/lws/v1"
]
}
접근 요청 및 접근 권한 부여 엔드포인트는 LWS 컨테이너이며 반드시 이 명세에 정의된 규칙을 준수해야 합니다.
이러한 엔드포인트는 최소한 GET 및 POST 작업을 지원해야 합니다. DELETE 작업의 지원은 권장됩니다. 다른 작업도 선택적으로 지원할 수 있습니다.
이러한 엔드포인트는 반드시 요청 및 응답 페이로드 모두에 대해 11.4.1 JSON-LD 직렬화에 정의된 JSON-LD 직렬화를 지원해야 합니다. 서버는 선택적으로 추가 직렬화를 지원할 수 있습니다.
에이전트는
POST 요청을 제출하여 접근 요청을 생성하며, 제출 대상은
해당
접근 요청 엔드포인트입니다. 저장소
제어자는 접근 권한 부여를 생성하며,
이를 위해 POST 요청을 접근 권한 부여 엔드포인트에 제출합니다.
성공한 POST 작업은 반드시 Location 헤더를 포함하여 응답해야 하며,
이 헤더의 값은
새 리소스의 URL로 설정해야 합니다.
POST / — 접근 요청을 생성합니다.GET / — 접근 요청 목록을 가져옵니다.GET /:id — 특정 접근 요청을 가져옵니다.DELETE /:id — 접근 요청을 취소합니다.POST / — 접근 권한 부여를 생성합니다.GET / — 접근 권한 부여 목록을 가져옵니다.GET /:id — 특정 접근 권한 부여를 가져옵니다.DELETE /:id — 접근 권한 부여를 철회합니다.
알림은 에이전트 및
저장소
제어자에게 다음 항목의 변경 사항을 알리는 메커니즘을 제공합니다:
접근 요청 및 접근 권한 부여.
inbox 속성이
접근 요청 또는 접근 권한 부여에 있는 경우,
서버는 가급적
해당 엔드포인트에 알림을 전달하여
할당 대상자에게 이벤트를 알리는 것이 좋습니다.
알림의 직렬화는 반드시 LWS 알림 데이터 모델의 요구 사항을 준수해야 합니다.
inbox 엔드포인트에 전달되는 알림은 반드시
알림 전달에 대해 LWS 프로토콜에 정의된 요구 사항을 준수해야 합니다.
이 절에서 설명하는 알림 메커니즘은
연결된 데이터
알림 패턴과 유사하며, 이 패턴에서는
알림을 발견할 때
inbox 속성을 사용하고, 전달할 때는 POST
요청을 해당 수신함에 전송합니다.
서버는 가급적 다음 이벤트에 대한 응답으로 알림을 전송하는 것이 좋습니다:
inbox에 지정된 위치이며,
관련 접근 요청에 명시됩니다.
서버는 선택적으로 다른 이벤트에 대한 응답으로 알림을 전송할 수 있습니다.
LWS 저장소 설명은 반드시 미디어 타입
application/lws+cid로 직렬화할 수 있어야 합니다.
application/lws+cid 미디어 타입은 다음 문서 형식을 특수화한 문서를 식별합니다:
W3C 제어 식별자 문서 [CID-1.0]. 이 문서는 LWS 어휘로 확장됩니다.
LWS 컨테이너 표현은 반드시 미디어 타입
application/lws+json을 지원해야 합니다.
LWS 컨테이너 표현은 JSON-LD 규칙을 사용하지만, LWS의 제약 조건과 요구 사항은 별도의 미디어 타입 사용을 정당화합니다.
URI https://www.w3.org/ns/lws/v1은 LWS 프로파일을 식별합니다 [RFC6906].
컨테이너의 표현은 다음 조건을 충족하면 LWS 프로파일에 부합합니다:
동일한 컨테이너 상태 및 페이지의 application/lws+json 표현과 RDF가
동형인 경우입니다. 즉, 인코딩된 RDF가
다음에 정의된 동형 관계를 충족해야 합니다: RDF 데이터셋
비교 [RDF12-CONCEPTS]. 비교 대상은
application/lws+json 표현을 JSON-LD로 해석할 때 인코딩되는 RDF입니다 [JSON-LD11].
application/lws+json
표현은 항상 LWS 프로파일에 부합합니다.
서버는 반드시 컨테이너에 대한 application/lws+json 요청을 처리해야 하며, 반드시
Content-Type 응답 헤더를 선택한 표현의 미디어 타입으로 설정해야 합니다.
클라이언트는 LWS 프로파일의 표현을 요청할 때 LWS 프로파일 URI를
RDF 미디어 타입의 profile 매개변수에 포함합니다. 이 매개변수를 정의하는 RDF 미디어 타입에는
application/ld+json
[JSON-LD11], text/turtle [RDF12-TURTLE], application/trig [RDF12-TRIG], application/n-triples [RDF12-N-TRIPLES], application/n-quads가
있습니다
[RDF12-N-QUADS]. 서버가 이러한 미디어 타입을 선택하면,
응답은 반드시 LWS 프로파일에 부합해야 합니다. 즉,
반드시 application/lws+json
표현과 RDF가 동형이어야 하며, 해당 Content-Type은 반드시 동일한
profile 매개변수를 포함해야 합니다. 서버는 절대로 이러한 미디어 타입을 선택해서는 안 됩니다.
해당 미디어 타입으로 LWS 프로파일에 부합하는 표현을 생성할 수 없는 경우가 이에 해당합니다.
선택한 표현이 요청의 Accept 헤더에 따라 달라지는 경우, 응답은 가급적
Vary: Accept 헤더를 포함하는 것이 좋습니다 [RFC9110].
컨테이너와 같은 일부 복합 리소스는 많은 수의 리소스를 보유할 수 있습니다. 클라이언트가 목록을 점진적으로 가져올 수 있도록, 서버는 가급적 페이지 나누기를 지원하는 것이 좋습니다. 이는 컨테이너의 멤버 수가 서버에서 정한 임곗값을 초과하는 경우에 해당합니다.
페이지 나누기는 링크를 기반으로 합니다. 서버는 HTTP Link 헤더로 페이지 나누기 URI를 제공합니다
[RFC8288].
이를 통해 클라이언트는 숫자 오프셋에 의존하지 않고 전체 목록을 탐색할 수 있습니다.
목록을 페이지로 나눈 경우, 응답 본문에는 현재 페이지의 항목만 포함됩니다.
복합 리소스의 id, type, totalItems 속성은
전체 멤버십을 반영하는 반면, items는
현재 페이지의 리소스만 포함합니다.
페이지 나누기 URI는 다음 표준 링크 관계를 사용하는 Link 헤더로
전달됩니다:
rel="first": 결과의 첫 번째 페이지 URI입니다. 반드시
페이지로 나눈 응답에 있어야 합니다.rel="last": 결과의 마지막 페이지 URI입니다. 선택적으로
페이지로 나눈 응답에 있을 수 있습니다.rel="next": 결과의 다음 페이지 URI입니다. 반드시 후속
페이지가 있을 때 존재해야 합니다. 마지막 페이지에서는 반드시 생략해야 합니다.rel="prev": 결과의 이전 페이지 URI입니다. 선택적으로
앞선
페이지가 있을 때 존재할 수 있습니다. 첫 번째 페이지에서는 반드시 생략해야 합니다.모든 페이지 나누기 URI는 클라이언트에 불투명합니다. 클라이언트는 가급적 페이지 나누기 URI를 구성하거나 수정하지 않는 것이 좋으며, 가급적 서버에서 제공하는 URI를 사용하는 것이 좋습니다.
클라이언트는 첫 번째 페이지를 얻기 위해 복합 리소스의 URI를 요청합니다. 응답에는 페이지 나누기 Link 헤더가 포함되며, 클라이언트는 이를 따라 후속 페이지를 가져옵니다. 서버는 선택적으로 다음 기능도 지원할 수 있습니다: 이전 탐색에서 얻은 페이지 나누기 URI를 통해 특정 페이지에 직접 접근하는 기능입니다.
페이지로 나눈 응답을 반환할 때, 서버는 반드시 200
OK로 응답해야 합니다. 응답 본문의 totalItems
속성은 가급적 현재 페이지의 항목
수뿐만 아니라 전체 페이지에 걸친 총 항목 수를 반영하는 것이 좋습니다.
요청:
GET /alice/photos/ HTTP/1.1
Authorization: Bearer <token>
Accept: application/lws+json
응답(첫 번째 페이지):
HTTP/1.1 200 OK
Content-Type: application/lws+json
ETag: "photos-page1-etag"
Link: </alice/photos/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
Link: </alice/photos/?page=1>; rel="first"
Link: </alice/photos/?page=3>; rel="last"
Link: </alice/photos/?page=2>; rel="next"
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/photos/",
"type": "Container",
"totalItems": 150,
"items": [
{
"type": "DataResource",
"id": "/alice/photos/vacation.jpg",
"format": "image/jpeg",
"size": 248392,
"modified": "2025-11-20T10:30:00Z"
},
{
"type": "DataResource",
"id": "/alice/photos/portrait.png",
"format": "image/png",
"size": 102400,
"modified": "2025-11-21T14:15:00Z"
}
]
}
요청(다음 페이지):
GET /alice/photos/?page=2 HTTP/1.1
Authorization: Bearer <token>
Accept: application/lws+json
응답(중간 페이지):
HTTP/1.1 200 OK
Content-Type: application/lws+json
ETag: "photos-page2-etag"
Link: </alice/photos/.meta>; rel="linkset"; type="application/linkset+json"
Link: </alice/>; rel="up"
Link: <https://www.w3.org/ns/lws#Container>; rel="type"
Link: </alice/photos/?page=1>; rel="first"
Link: </alice/photos/?page=1>; rel="prev"
Link: </alice/photos/?page=3>; rel="next"
Link: </alice/photos/?page=3>; rel="last"
{
"@context": "https://www.w3.org/ns/lws/v1",
"id": "/alice/photos/",
"type": "Container",
"totalItems": 150,
"items": [
{
"type": "DataResource",
"id": "/alice/photos/sunset.jpg",
"format": "image/jpeg",
"size": 315000,
"modified": "2025-11-22T09:00:00Z"
}
]
}
LWS JSON-LD 맥락은 URL https://www.w3.org/ns/lws/v1로 식별됩니다.
해당 URL을 해석하여 얻은 문서의 SHA2-256 다이제스트는 아래 표에 포함되어 있습니다:
| 맥락 URL 및 해시 |
|---|
URL: https://www.w3.org/ns/lws/v1 |
| SHA2-256 다이제스트: 할 일: 맥락 문서가 확정되면 JSON-LD 맥락 다이제스트를 포함합니다 |
다이제스트는 다음과 같은 명령으로 검증할 수 있습니다:
curl -sL -H "Accept: application/ld+json" https://www.w3.org/ns/lws/v1 | openssl dgst -sha256
URI 스킴을 포함하여 LWS 프로토콜 내에서 리소스를 식별하고 주소를 지정하는 방법, 리소스 명명 규칙, 해석 메커니즘을 정의합니다. 이 절은 다른 절 안으로 이동할 수 있습니다. 예를 들어 리소스 접근 절로 이동할 수 있습니다
이 부분은 의도적으로 비워 두었습니다
LWS 구현이 서로 다른 플랫폼, 환경, 저장소 백엔드에서 상호운용성을 유지하며 작동하도록 보장하기 위한 고려 사항을 설명하고, 저장소 제공자를 변경할 수 있도록 지원하는 수단을 제공합니다
이 부분은 의도적으로 비워 두었습니다
이 절은 비규범적입니다.
위협 모델, 보안 요구 사항, 안전한 LWS 배포를 위한 구현 지침을 다루는 정식 보안 고려 사항 절입니다.
OAuth 2.0 보안을 위한 현행 최선 관행 [RFC9700]에 설명된 권장 사항은 이 명세에 적용됩니다.
이 절은 비규범적입니다.
전송 계층 보안(TLS)은 변조, 위장, 정보 유출을 방지하는 중요한 메커니즘입니다. TLS로 보호되는 통신은 [RFC6125]에 따라 검증할 수 있습니다. 구현 보안 고려 사항은 "전송 계층 보안(TLS) 및 데이터그램 전송 계층 보안(DTLS)의 안전한 사용을 위한 권장 사항" [RFC9325]에서 확인할 수 있습니다.
이 절은 비규범적입니다.
베어러 토큰과 디지털 자격 증명은 탈취 및 재전송에 취약합니다. 완화책에는 적절히 짧은 유효 기간 사용, 특정 수신 대상에 토큰 바인딩, 안전한 토큰 저장이 포함됩니다.
aud
클레임 등을 통해 토큰이 다른 서버에서 재전송되는 것을 방지할 수 있습니다. 접근 토큰의
수신 대상에
둘 이상의 서버가 포함되면, 해당 서버 중 어느 것이든 다른 서버에 그 토큰을 재전송할 수 있습니다. 인가
서버는
단일 저장소 URI를 수신 대상으로 하는 토큰을 발급하여 이를 방지합니다. 수신 대상이
제한되지 않은
자격 증명을 처리하는 애플리케이션은 이러한 자격 증명을 다른 개체에 전송할 때 주의해야 하며,
특히 신뢰할 수 없는
인가 서버에 전송할 때 주의해야 합니다. 자격 증명 발급자가 토큰의 수신 대상을 제한할 수 없다면, 클라이언트는
다음과 같은
메커니즘을 사용할 수 있습니다: OAuth 2.0 토큰 교환 [RFC8693]. 이를 통해
수신 대상이 제한된 자격 증명을 생성한 후
다른 보안 도메인의 인가 서버와 상호작용할 수 있습니다.
dpop_signing_alg_values_supported 및
dpop_bound_access_tokens_required 매개변수를 사용하여 알릴 수 있습니다. 이 매개변수는 [RFC9728]에 정의되어 있습니다.
localStorage, URL,
로그에 저장할 때마다 해당 토큰은 유출 공격에 더 취약해집니다.
이 절은 비규범적입니다.
클라이언트는 저장소 서버가 보낸 응답을 통해 인가 서버의 위치를 알게 되며, 클라이언트는 해당 저장소 서버를 신뢰하지 않을 수도 있습니다. 이 발견 과정에는 다음 고려 사항이 적용됩니다.
Link 헤더에서 저장소 URI를 얻습니다.
악의적인 서버는 다른 저장소의 URI를 제공하여 클라이언트가
해당 저장소의 접근 토큰을 얻고
이를 악의적인 서버에 제시하게 할 수 있으며, 해당 서버는 그 토큰을 재전송할 수 있습니다. 따라서
클라이언트는
접근 토큰을 요청하기 전에 저장소 설명을 사용하여,
요청한 URI가
저장소 루트와 동일한 출처에서 제공되는지 확인해야 합니다. 이는
다음에
설명되어 있습니다: 5.2.3
클라이언트의 발견 검증.
resource_metadata URL,
저장소
URI가 포함된
Link 헤더, 보호 리소스 메타데이터 문서의 발급자 식별자는
저장소 서버가
선택하며, 클라이언트는 이들이 식별하는 문서를 가져옵니다. 이러한
가져오기 작업을 다른 당사자를 대신하여
수행하는 클라이언트(예: 서버 측 애플리케이션)는 서버 측 요청 위조에 노출됩니다. 이는
다음에
설명되어 있습니다: [RFC9728]의 7.7절. 예방 조치에는
가져오기를 https 스킴으로 제한하고
내부 IP 주소 범위에 대한 요청을 차단하는 것이 포함됩니다.
이 절은 비규범적입니다.
assignee 값이
요청 에이전트의 인증된 신원과 연관되어 있는지 검증하는 것이 권장됩니다.
이 절은 비규범적입니다.
이 절은 비규범적입니다.
up 링크를 변경하면
해당 리소스에 접근할 수 있는 에이전트의 집합이 넓어지거나 좁아질 수 있으며, 이는 정책을 편집하지 않고
또한
리소스의 콘텐츠를 변경하지 않아도 발생합니다. 이러한 결과를
저장소 제어자와 이동을 요청하는 에이전트에게
명확히 보여 주는 것은 유용한
완화책입니다. 다만 구현에서 이를 수행하는 방법은 이
명세의 범위를 벗어납니다.
MoveResource
기능을 기술하면 평소 서버가 자체적으로 유지하는 up 링크를
클라이언트가 쓸 수 있게 됩니다. 서버는 가급적 해당 기능을
배포 환경에서
필요로 하는 곳에만 기술하는 것이 좋으며, 가급적 appliesTo 속성을
지원해야 하는
리소스 타입으로 제한하는 것이 좋습니다. 또한 반드시 클라이언트가
수정하도록 허용되지 않은
링크를 계속 보존해야 합니다.
이 절은 비규범적입니다.
데이터 최소화, 사용자 동의, 개인정보를 보호하는 구현 패턴을 포함한 LWS 프로토콜의 개인정보 보호 영향을 다룹니다.
자격 증명은 사용자 및 에이전트에 관한 정보를 담고 있습니다. 디지털 서명은 변조를 방지할 수 있지만, 클라이언트나 제삼자는 암호화되지 않은 자격 증명 내부의 값을 읽을 수 있습니다.
따라서 자격 증명 발급자는 필요한 정보만 포함하는 토큰을 생성하는 것이 권장됩니다. 그 용도는 인증 또는 인가이며, 필수적인 경우가 아니면 민감한 속성을 포함하지 않는 것이 좋습니다.
일반적으로 암호화되지 않은 자격 증명 데이터를 로그에 기록하는 것은 안티패턴입니다. 이것이 필요한 경우, 구현은 자격 증명을 잘라내거나 해시 처리하여 자격 증명 주체의 개인정보를 보호할 수 있습니다.
JWT에서 가명 식별자를 사용하더라도, 저장소 서버는 여전히 동일한 에이전트의 요청을 시간에 걸쳐 연관 지을 수 있습니다. 이 경우 사용자 개인정보를 보호하기 위해 클라이언트 애플리케이션은 JWT의 일괄 발급을 요청하여, 각 JWT를 한 번만 사용할 수 있습니다. 그러나 이렇게 해도 저장소 서버가 JWT 콘텐츠의 유사성이나 발신 IP 주소와 같은 다른 정보를 사용하여 요청을 연관 짓는 것을 막지는 못합니다. 가명 식별자를 사용할 때 인가 서버는 동일한 식별자를 두 번 이상 발급하지 않도록 주의해야 합니다.
이 절은 비규범적입니다.
이 절은 비규범적입니다.
actor 속성을 포함하면
변경한 사람이 누구인지 드러납니다. 이는 일부 상황에서 부적절할 수 있습니다.
actor 속성은 가급적 기본적으로 생략하는 것이 좋습니다. 서버는 선택적으로
해당 속성의 포함 여부를 설정할 수 있도록 할 수 있습니다.
이 절은 비규범적입니다.
up 링크가
변경되기 전에 적용 정책의 차이를 드러내는 것은 유용한 완화책이지만, 클라이언트가 이를 수행하는 방법은
이 명세의 범위를 벗어납니다.
이 고려 사항의 구체적인 예시는 작업 그룹이 저장소에서 식별자 할당 방식을 기술하는 방법을 결정한 뒤에 제시하는 것이 가장 좋습니다.
이 절은 비규범적입니다.
이 명세는 "잘 알려진 URI" 레지스트리에 다음 값을 추가합니다. 이 레지스트리는 RFC 5785 [RFC5785]에 의해 수립되었습니다.
이 명세는 application/lws+json 미디어 타입을
문서 식별을 위해 등록하며,
구체적으로 Linked Web Storage 컨테이너 형식에 부합하는 문서를 식별합니다.
application/lws+json 미디어 타입을 사용하는 리소스는
다음을 준수해야 합니다:
application/json 미디어 타입의 모든 요구 사항입니다. 따라서 동일한 인코딩
고려 사항이
적용되며, 이는 [RFC8259]의 11절에 명시되어 있습니다.
Linked Web Storage 형식은 JSON-LD 규칙을 사용하지만, 여러 제약 조건과 추가 요구 사항이 LWS 구현에 적용되므로 별도의 미디어 타입을 사용하는 것이 타당합니다.
이 명세는 application/lws+cid 미디어 타입을 다음 문서를 식별하기 위해 등록합니다:
Linked Web Storage 프로토콜에 부합하며
W3C 제어 식별자 1.0 문서 형식을 특수화한 저장소 설명 문서입니다.
application/lws+cid 미디어 타입을 사용하는 리소스는
application/json의 모든 요구 사항을 준수해야 합니다.
따라서 해당 미디어 타입과 동일한 인코딩 고려 사항이 적용되며, 이는
[RFC8259]의 11절에 명시되어 있습니다.
이 절은 비규범적입니다.
이 명세는 Solid 프로토콜 [Solid-Protocol]을 상당 부분 참고하였으며, 해당 프로토콜은 오랜 기간에 걸쳐 다음 사람들이 편집했습니다: Sarven Capadisli, Tim Berners-Lee, Kjetil Kjernsmo, Ruben Verborgh, Justin Bingham, Dmitri Zagidulin.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: