EDITORCLASSBLOGLOGINimg default descriptionimg default descriptionimg default description표현한다는 것의무한한 가능성,새로운 형태로
담아내다.
img default descriptionimg default descriptionimg default descriptionimg default descriptionimg default descriptionimg default descriptionimg default description새로운 형태의 콘텐츠Opkle은 코드 없이 웹을 마음껏 만들고, 누구나 자기 화면을 그릴 수 있도록 에디터를 만들고, 그 결과물을 어디서나 즐길 수 있게 돕는 팀입니다.텍스트와 화면과의 조화를 통해, 웹을 짓는다는 것이 그저 단순한 코딩이 아닌, 상상을 펼치고 감각을 깨우는 과정이 될 수 있도록 좋은 도구를 만들어 냅니다.
옵클 에디터 개발기: 그래픽 엔진 안의 선형대수dev12옵클 에디터 개발기: 인디자인처럼 편집하는 레이아웃 편집기dev13옵클 에디터 개발기: 원고가 다음 페이지로 흐르는 편집기dev14옵클 에디터 개발기: EPUB과 IDML을 편집 가능한 페이지로dev15옵클 에디터 개발기: 한 번 만든 레이아웃을 EPUB과 인쇄물로dev1689101112...14 옵클 에디터 개발기: 원고가 다음 페이지로 흐르는 편집기페이지 공간을 완성하고 나니 레이아웃 에디터의 다음 성격이 분명해졌습니다. 도형과 이미지를 정확한 위치에 놓는 것만으로는 책을 편집할 수 없습니다. 원고는 길이가 달라지고, 글꼴과 행간, 판형이 바뀔 때마다 줄과 문단, 페이지의 경계가 함께 이동합니다. 조판은 좌표를 한 번 계산하는 일이 아니라 여러 제약을 만족하는 배치를 계속 다시 구하는 문제였습니다.브라우저에도 흐름형 layout engine이 있지만 출판 조판은 목적함수가 조금 다릅니다. 화면은 가용 폭에 맞춰 무한히 아래로 늘어날 수 있지만 책은 유한한 page box들의 열로 나뉩니다. 한 문단을 어디에서 자를지, 고아줄과 과부줄을 얼마나 피할지, 그림과 캡션을 같은 페이지에 둘지에 따라 가능한 해가 여러 개 생깁니다. 단순한 overflow 판정만으로는 좋은 조판을 정의할 수 없었습니다.저는 이 문제를 텍스트를 상자 사이로 옮기는 기능보다 composition system으로 보았습니다. 자유 좌표와 흐름형 구조, 문단 의미와 물리 페이지가 하나의 제약계 안에서 만납니다. 입력 문자열이 같아도 글꼴 metric과 폭이 달라지면 해가 달라지고, 같은 조건에서는 언제 계산해도 같은 결과가 나와야 합니다. 이 단계에서는 조판을 그래프, 최적화와 고정점 계산의 언어로 다시 정리했습니다.Formatting context책의 페이지는 절대 좌표만으로 구성되지 않습니다. 이미지와 설명이 나란히 놓이거나 여러 항목이 일정한 간격으로 이어지고, 폭이 줄면 다음 줄로 넘어가야 하는 영역도 있습니다. 이런 배치는 좌표 목록보다 formatting context로 설명하는 편이 정확합니다. 컨테이너가 주축과 교차축을 정하고 자식은 자신의 최소 크기, 선호 크기와 늘어날 수 있는 정도를 제약으로 제출합니다.가용 길이를 A, 자식의 기본 길이를 `b_i`, 증가 계수를 `g_i`, 감소 계수를 `s_i`라고 보면 배치는 `Σb_i`와 A의 차이를 자식들에게 분배하는 문제입니다. 여유가 있으면 grow 비율에 따라 나누고 부족하면 shrink와 기본 크기를 함께 고려해 줄입니다. 다만 최소 콘텐츠 크기 아래로 내려갈 수 없는 항목이 생기면 남은 항목을 다시 계산해야 하므로 단순한 한 번의 비례식보다 active-set 문제에 가깝습니다.줄바꿈이 허용되면 문제는 1차원 분배에서 구간 분할로 바뀝니다. 자식 순서를 유지한 채 어느 지점에서 다음 줄을 시작할지 결정하고, 각 줄마다 남는 공간과 정렬을 다시 계산합니다. 가능한 분할 수는 항목 수에 따라 빠르게 늘지만, 실제 에디터에서는 국소적인 greedy 해와 최소 크기 제약만으로도 예측 가능한 결과를 만들 수 있습니다. 중요한 것은 계산 결과와 사용자가 지정한 제약을 구분하는 일이었습니다.이 관점에서는 fixed 페이지 안의 흐름도 특수한 예외가 아니었습니다. 자유 좌표는 위치를 직접 결정하는 강한 제약이고, 흐름형 컨테이너는 관계를 통해 위치를 결정하는 약한 제약입니다. 둘 다 결국 장면 그래프의 변환과 크기 제약으로 표현됩니다. 도구가 달라 보여도 내부 수학은 변수와 제약, 계산된 해의 조합으로 정리되었습니다.Constraint space자유 배치와 흐름형 배치의 차이는 오브젝트의 종류보다 좌표를 누가 결정하느냐에 있습니다. 자유 배치에서는 `(x, y, w, h)`가 독립 변수에 가깝고, 흐름형 배치에서는 순서와 최소 크기, 정렬 규칙이 주어지면 좌표는 계산 결과가 됩니다. 한 표현에서 다른 표현으로 옮긴다는 것은 DOM을 바꾸는 일이 아니라 독립 변수와 종속 변수의 역할을 바꾸는 일입니다.자유 배치 결과에서 구조를 추론하는 문제는 본질적으로 역문제입니다. 같은 화면은 여러 구조로 설명될 수 있습니다. 나란히 놓인 두 요소가 하나의 row일 수도 있고 우연히 같은 y좌표를 가진 두 독립 객체일 수도 있습니다. 관측된 geometry만으로 원래 의도를 유일하게 복원할 수 없으므로, 최소 설명 길이와 안정성 같은 기준이 필요합니다. 적은 규칙으로 현재 배치를 설명하면서 화면 폭이 달라져도 관계가 보존되는 구조가 더 좋은 후보가 됩니다.반대로 흐름형 구조를 자유 좌표로 확정하는 과정은 forward evaluation입니다. 제약계를 한 번 평가해 나온 box geometry를 문서 공간의 좌표로 바꾸면 됩니다. 이때 화면 확대와 viewport 이동은 관찰 변환일 뿐이므로 역행렬로 제거되어야 합니다. 결과 좌표는 사용자가 보고 있던 화면이 아니라 문서가 정의한 기준 공간에 속해야 합니다.이 왕복은 완전한 역함수가 아닙니다. 자유 좌표로 확정하는 순간 줄바꿈 가능성과 상대 정렬 같은 정보가 사라질 수 있고, 구조를 다시 추론할 때는 동일한 해를 보장할 수 없습니다. 그래서 변환을 정보 보존의 관점에서 보고, 손실되는 자유도와 새로 생기는 제약을 명확히 구분했습니다. 종이 조판과 반응형 문서 사이의 관계도 같은 역문제와 순방향 계산의 조합으로 이해할 수 있었습니다.Story graph본문 조판은 원고의 논리적 순서와 그것을 담는 물리 영역의 관계에서 시작했습니다. 원고는 하나의 선형 sequence지만 지면은 여러 개의 상자로 나뉩니다. 이를 그래프로 보면 텍스트 구간은 순서를 가진 정점이고, 프레임은 용량을 가진 구획이며, 조판은 순서를 보존하면서 구간을 구획에 대응시키는 함수입니다. 화면상의 페이지가 나뉘어도 원고의 위상적 순서는 끊기면 안 됩니다.그래프에서 필요한 핵심 불변식은 연결성과 비순환성입니다. 하나의 story가 분기하거나 cycle을 가지면 다음 문장이 두 곳으로 갈라지거나 끝없이 같은 프레임을 순환할 수 있습니다. 따라서 유효한 흐름은 일반 그래프보다 경로 graph에 가깝고, 각 정점의 in-degree와 out-degree에는 강한 제한이 생깁니다. 이 조건은 특정 저장 방식이 아니라 조판 모델 자체의 수학적 성질입니다.편집으로 프레임이 추가되거나 제거될 때도 보존해야 할 것은 화면 객체보다 원고 sequence입니다. 그래프 변환 전후에 모든 문자와 문단의 상대 순서가 같아야 하고, 어떤 구간도 중복되거나 유실되지 않아야 합니다. 이를 multiset 보존과 order-preserving mapping으로 볼 수 있습니다. 구조 변화가 커질수록 개별 포인터보다 이 전역 불변식을 검증하는 편이 더 강합니다.부분만 관측된 그래프에서는 전역 조판을 확정할 수 없습니다. 이는 분산 시스템에서 incomplete view로 전체 상태를 판단할 수 없는 것과 비슷합니다. 필요한 구간이 준비되지 않았다면 계산을 보류하고, 완전한 경계 조건이 갖춰졌을 때 같은 story를 한 번 평가하는 편이 안전합니다. 대형 문서에서 계산량을 줄이는 일도 결국 그래프의 어느 부분이 현재 해에 영향을 주는지 찾는 문제였습니다.Line breaking줄바꿈은 문자를 폭만큼 채우다가 넘치면 자르는 문제가 아니었습니다. 실제 입력은 글자보다 glyph sequence에 가깝습니다. ligature와 kerning, 한글 조합과 script shaping을 거치면 문자 수와 표시되는 glyph 수가 달라지고, 줄의 폭은 각 glyph advance와 조정 가능한 공백의 합으로 결정됩니다. 먼저 언어적 경계와 조판 가능한 break opportunity를 구분해야 했습니다.한 줄의 품질은 남는 공간 하나로 평가할 수 있습니다. 목표 폭을 W, 줄의 자연 폭을 N, 늘어날 수 있는 공백을 S, 줄어들 수 있는 양을 R이라고 하면 adjustment ratio는 남는 공간의 부호에 따라 `(W-N)/S` 또는 `(W-N)/R`로 볼 수 있습니다. 이 비율이 너무 크면 공백이 벌어지고 너무 작으면 글자가 답답해집니다. badness를 ratio의 세제곱처럼 두면 조금 나쁜 줄 여러 개와 매우 나쁜 줄 하나를 구분할 수 있습니다.문단 전체의 줄바꿈은 가능한 경계들을 정점으로 둔 DAG의 최단경로 문제로 바뀝니다. 두 break point 사이가 한 줄이 될 수 있다면 edge를 만들고, 그 줄의 badness와 hyphen penalty를 비용으로 둡니다. 동적 계획법으로 전체 비용이 가장 작은 경로를 찾으면 마지막 줄 하나만 맞추는 greedy 방식보다 문단 전체의 회색도가 고르게 나옵니다. Knuth-Plass 계열 조판이 강한 이유도 줄 하나가 아니라 문단 전체를 최적화하기 때문입니다.페이지 경계에서는 비용 함수가 더 늘어납니다. 문단의 첫 줄이나 마지막 한 줄만 다른 페이지에 남는 경우, 제목이 본문과 갈라지는 경우, 각주가 참조보다 앞에 나타나는 경우에는 별도의 penalty가 필요합니다. 조판은 문자열 분할이 아니라 여러 의미 제약을 가진 최적화가 됩니다. 같은 입력과 metric에서는 같은 최소 비용 해를 선택하도록 tie-break 규칙까지 결정해야 결과가 재현 가능했습니다.Layout fixed point조판은 한 방향으로 내용이 밀려나는 과정처럼 보이지만 수학적으로는 안정 상태를 찾는 반복 계산에 가깝습니다. 현재 배치를 `x_n`, 조판 연산을 F라고 하면 다음 상태는 `x_(n+1) = F(x_n)`입니다. 더 이상 줄과 페이지 경계가 달라지지 않는 `x = F(x)`가 fixed point입니다. 글자가 늘거나 줄어도 같은 연산을 반복해 새로운 안정점으로 수렴해야 합니다.수렴을 보장하려면 상태 변화에 단조성이 필요합니다. overflow를 줄이기 위해 뒤쪽으로 보낸 내용이 같은 pass 안에서 임의의 규칙으로 다시 앞으로 돌아오면 oscillation이 생길 수 있습니다. 계산 방향과 우선순위를 고정하고, 각 단계에서 해결하려는 위반량이 감소하도록 potential function을 둘 수 있습니다. 예를 들어 모든 프레임의 초과 높이와 비어 있는 앞쪽 공간에 가중치를 준 합이 반복마다 감소하는지를 보는 식입니다.그러나 글꼴이 늦게 바뀌거나 이미지 크기가 비동기로 확정되면 함수 F 자체가 계산 중에 달라집니다. 움직이는 목적함수에서는 수렴을 논할 수 없으므로 metric과 자산이 준비된 하나의 layout epoch를 먼저 확정해야 합니다. 같은 epoch 안에서는 입력과 제약을 불변으로 두고, 새로운 정보가 도착하면 이전 계산을 수정하는 대신 새 epoch에서 다시 평가합니다.종료 조건도 단순한 반복 횟수보다 의미 있는 상태 차이로 잡는 편이 낫습니다. 페이지별 break point와 overset 집합이 같아졌다면 geometry의 작은 부동소수점 오차가 남아도 조판 결과는 안정된 것으로 볼 수 있습니다. 반대로 break point가 두 상태 사이를 왕복하면 cycle을 발견해 더 안전한 제약으로 내려가야 합니다. 이론적으로 완벽한 해보다 유한 시간 안에 결정적인 해를 내는 것이 편집기의 중요한 조건이었습니다.Fragmentation context프레임을 넘어간 문단의 뒷부분은 새로운 문단이 아닙니다. 논리 트리에서는 하나의 block이지만 물리 조판에서는 여러 fragment로 나타납니다. 이때 margin과 첫 줄 들여쓰기, drop cap처럼 문단의 시작에만 적용되는 속성과 각 fragment마다 반복되어야 하는 배경·테두리를 구분해야 합니다. fragmentation context는 논리 객체와 물리 조각이 일대일이라는 가정을 버리는 데서 시작합니다.이를 함수로 보면 하나의 box B가 페이지 제약 P에 의해 `F_P(B) = {b_1, b_2, ..., b_k}`로 분해됩니다. 조각들의 콘텐츠를 순서대로 이어 붙이면 원래 B가 되어야 하고, 각 조각의 내부 구간은 서로 겹치지 않아야 합니다. 즉 fragmentation은 순서를 보존하는 partition입니다. 페이지 폭이 바뀌면 과거 조각을 다시 자르는 것이 아니라 원래 B에 새로운 P를 적용해야 멱등성이 유지됩니다.스타일도 속성별로 fragmentation law가 다릅니다. 첫 줄 스타일은 첫 조각의 첫 line box에만 적용되고, 문단 앞 간격은 첫 조각에, 뒤 간격은 마지막 조각에 속합니다. 반면 line-height와 글꼴은 모든 조각에 상속됩니다. 이 규칙을 개별 예외로 흩어놓기보다 속성이 `first`, `last`, `each`, `slice` 중 어떤 반복 의미를 가지는지 분류하는 편이 일반적입니다.과부줄과 고아줄은 이 partition에 대한 전역 제약입니다. 단순히 현재 페이지에 들어가는 최대 줄 수를 택하면 다음 페이지에 한 줄만 남을 수 있습니다. 최소 시작 줄과 최소 끝 줄을 만족하도록 break point를 앞쪽으로 이동시키면 현재 페이지에 여백이 생기더라도 문단의 읽기 리듬이 좋아집니다. 조판의 정확성은 공간을 끝까지 채우는 것보다 논리 구조를 물리 조각에 올바르게 보존하는 데 있었습니다.Composition state조판 중인 텍스트에는 완성된 문서 상태만 존재하지 않습니다. 특히 한글과 일본어, 중국어 입력기는 여러 키 입력을 하나의 확정 문자열로 합성하는 중간 상태를 가집니다. 이 상태는 아직 문서에 완전히 커밋되지 않았지만 화면에는 보여야 합니다. 편집기는 committed text와 composing text를 서로 다른 수명의 상태로 다뤄야 했습니다.입력 과정을 상태기계로 보면 `idle -> composing -> committed`의 전이가 생깁니다. composing 중에는 브라우저와 입력기가 임시 문자열과 선택 범위를 공동으로 관리하므로 외부 조판기가 트리를 재구성하면 전이의 소유권이 깨집니다. 중간 상태에서는 구조 변경을 제한하고, 확정 이벤트가 온 뒤 논리 문서에 반영하는 것이 언어에 상관없이 안전한 원칙입니다.caret도 DOM node의 주소보다 원고 sequence 위의 위치로 보는 편이 정확합니다. 화면 트리는 조판에 따라 사라지고 다시 생길 수 있지만, 논리 위치는 문서 시작부터의 경계나 인접한 안정 토큰 사이의 position으로 표현할 수 있습니다. 재조판 전후의 트리가 달라도 같은 논리 경계를 새 fragment에서 다시 찾으면 편집 감각이 이어집니다. 이는 syntax editor가 source offset과 syntax node를 구분하는 것과 비슷합니다.동시성 관점에서는 입력과 layout이 서로 다른 transaction입니다. 입력 transaction이 아직 확정되지 않았는데 layout이 중간 값을 권위적인 원고로 읽으면 같은 이벤트가 두 번 반영되거나 조합 문자열이 분리될 수 있습니다. 입력의 commit boundary를 먼저 정의하고 조판은 마지막으로 확정된 revision만 읽게 했습니다. 편집 안정성과 최종 조판의 정확성은 이 revision 경계를 통해 함께 유지되었습니다.Pagination optimization페이지네이션은 line break보다 큰 단위의 partition 문제였습니다. 문단과 그림, 표를 순서대로 나열한 뒤 각 페이지의 수용 높이를 넘지 않도록 구간을 나눠야 합니다. 단순 bin packing과 달리 항목 순서를 바꿀 수 없고, 일부 항목은 분할 가능하며, 제목과 다음 문단처럼 함께 있어야 하는 관계도 있습니다. ordered capacitated partition에 의미 penalty가 붙은 형태였습니다.가능한 page break를 정점으로 두면 다시 DAG 최단경로로 볼 수 있습니다. 구간 `(i, j)`가 한 페이지에 들어가면 edge를 만들고, 남는 여백의 제곱, widow와 orphan, keep-with-next 위반과 강제 분할에 비용을 줍니다. 전체 비용이 작은 경로를 택하면 현재 페이지를 가장 꽉 채우는 greedy 해보다 뒤쪽 페이지까지 고르게 배치할 수 있습니다.부동 요소와 각주가 들어오면 제약은 상호 의존합니다. 본문을 어느 페이지에 놓느냐가 각주 높이를 정하고, 각주 높이는 다시 본문 수용량을 줄입니다. 이 경우 본문 배치와 부가 영역의 높이를 번갈아 계산해 fixed point를 찾거나, 일정 횟수 안에 수렴하지 않으면 더 보수적인 break를 선택해야 합니다. 모든 경우의 최적해보다 결정적이고 설명 가능한 해가 편집기에는 더 중요했습니다.페이지 추가와 제거는 이 최적화 결과의 부산물입니다. 사용자의 한 번의 입력이 여러 page break를 바꾸더라도 의미상 하나의 문서 변환으로 보아야 합니다. 조판 결과의 개별 DOM 변화가 아니라 입력 revision과 새 partition 사이의 차이를 하나의 명령으로 다루면 undo에서도 원고와 구조가 같은 시간축을 유지할 수 있습니다.Serializable history멀티페이지 에디터의 history는 화면별 stack이 아니라 문서 전체의 논리적 시간이어야 했습니다. 한 명령이 여러 페이지와 자산을 동시에 바꿀 수 있으므로 부분 상태만 되돌리면 존재하지 않는 참조나 서로 다른 revision이 섞입니다. 데이터베이스의 transaction처럼 한 동작의 모든 write가 함께 보이거나 전혀 보이지 않아야 합니다.history 표현에는 snapshot과 operation log라는 두 극단이 있습니다. snapshot은 복원이 단순하지만 큰 문서에서 공간을 많이 쓰고, operation은 작지만 역연산이 항상 존재하지 않습니다. 회전처럼 가역적인 명령도 외부 자산 교체나 자동 조판을 거치면 같은 연산을 반대로 적용하는 것만으로 원래 byte를 보장할 수 없습니다. 실제 시스템은 의미 명령과 필요한 범위의 snapshot을 섞는 hybrid가 자연스럽습니다.직렬성은 여러 비동기 작업에도 필요합니다. 사용자 입력 A와 자동 조판 B, 저장 C가 겹쳤을 때 최종 상태가 어떤 순차 실행과도 같지 않다면 history와 영속 상태가 갈라집니다. revision을 기준으로 각 작업이 읽은 상태와 쓰려는 상태를 확인하고, 오래된 revision에서 시작한 결과는 새 상태 위에 덮지 않아야 합니다. 이는 낙관적 동시성 제어와 같은 문제였습니다.자동 저장의 목적은 모든 중간 화면을 기록하는 것이 아니라 마지막으로 커밋된 revision을 durable하게 만드는 것입니다. 저장 중 새 revision이 생겼다면 두 저장을 동시에 경쟁시키기보다 이전 write가 끝난 뒤 최신 revision을 다시 확정하는 편이 순서를 보장하기 쉽습니다. history의 head와 저장된 revision이 어떤 관계인지 명시하면서 undo, redo와 재시작 복원이 하나의 시간 모델 안으로 들어왔습니다.Working set페이지가 수백 장으로 늘어나면 모든 장면과 글꼴, 이미지를 동시에 메모리에 올리는 방식은 유지할 수 없습니다. 여기서 필요한 개념은 문서 전체와 현재 working set의 분리였습니다. 사용자가 보고 있거나 곧 볼 가능성이 높은 페이지, 편집 중인 story에 영향을 주는 페이지와 최근 history가 참조하는 상태만 빠른 계층에 유지하면 됩니다.working set은 단순히 화면과 교차하는 사각형의 집합보다 넓습니다. 현재 페이지의 이웃은 곧 탐색될 확률이 높고, 연결된 텍스트의 앞뒤 구간은 재조판에 필요합니다. 반대로 화면에 보이더라도 아주 작게 축소된 수십 페이지의 세부 DOM을 모두 유지할 필요는 없습니다. 공간적 거리, 시간적 locality와 의미적 의존성을 함께 사용해야 캐시 정책이 편집 감각과 맞습니다.eviction은 메모리를 비우는 동작이 아니라 권위적인 상태가 다른 계층에 안전하게 존재한다는 증명 뒤에 가능한 동작입니다. dirty state, 계산 중인 값과 undo가 참조하는 값은 pinning되어야 하고, 재구성 비용이 큰 항목은 단순 LRU보다 오래 남길 가치가 있습니다. 비용 가중 캐시에서는 항목 크기와 재생성 시간, 접근 확률을 함께 고려합니다.전체 출력과 전역 재조판은 working set 최적화가 적용되지 않는 별도의 계산입니다. 이때는 모든 의미 상태가 필요하지만 모든 편집용 UI가 필요한 것은 아닙니다. 문서 모델을 읽는 일과 화면 객체를 모두 만드는 일을 분리하면 전체 계산도 메모리 상한 안에서 streaming할 수 있습니다. 대형 문서의 성능은 빠른 함수 하나보다 어떤 상태를 언제 물질화하는지에 달려 있었습니다.조판 엔진 완성이 단계에서 레이아웃 에디터는 페이지 위에 글상자를 놓는 도구를 넘어섰습니다. 자유 좌표와 formatting context를 하나의 제약계로 보고, 원고 순서는 비순환 story graph로, 줄바꿈과 페이지네이션은 penalty를 가진 최단경로 문제로 정리했습니다. 문단은 물리 페이지에서 여러 fragment가 되어도 논리적으로 하나라는 불변식을 유지했습니다.조판은 반복 연산의 fixed point로 수렴하고, 입력기의 composition state와 caret은 물리 DOM이 아니라 논리 문서 revision에 연결했습니다. history와 자동 저장은 serializable transaction으로 보고, 대형 문서는 공간·시간·의미 locality를 반영한 working set만 물질화했습니다. 서로 달라 보이던 기능들이 그래프와 최적화, 트랜잭션과 캐시 이론 안에서 하나의 시스템이 되었습니다.짧은 문단과 매우 긴 문단, 여러 문자 체계, 극단적인 글꼴 metric, 고아줄과 과부줄, 판형 변경과 대형 문서를 조합해 반복 검수했습니다. 같은 입력과 제약에서는 같은 break point가 만들어지고, revision 전환과 저장, 메모리 압박 중에도 원고의 순서와 문단 의미가 유지되는 것을 모두 확인했습니다. 원고가 페이지를 건너가면서도 논리 구조를 잃지 않는 조판 엔진은 이 단계에서 완전히 구현되고 안정화되었습니다.이전글목록으로다음글저작권 고시Copyright Notice본 웹사이트의 모든 디자인 결과물 및 영상에 대한 저작권은 Abstract Cloud에 있으며, 저작권법 및 관련 법령에 의해 보호받습니다. 웹, 영상, 본문, 표지, 내지 디자인을 포함한 모든 콘텐츠는 저작권자의 자산으로, 사전 동의 없이 무단 복제, 배포, 2차 저작물 제작, 온라인 공유 등을 금지합니다. 이를 위반할 시, 저작권법에 따라 민형사상 책임을 질 수 있습니다. 정당한 구매와 저작권 보호는 창작자의 권리를 지키며, 더 나은 작품으로 보답할 힘이 됩니다.저작권자: Abstract Cloud | 대표자: 배창규(uragen)
© Abstract Cloud. All Rights Reserved.
HOMEFAQ이용 약관개인정보 이용방침help@opkle.app
010-2747-3403
상호 :추상적 형상 디자인(Abstract cloud)  |  대표자 :배창규사업자등록번호 :249-74-00533통신판매업 신고번호 :2025-의정부송산-0634주소 :경기도 의정부시 부용로 49, 108동 402호웹의 모든 콘텐츠, 디자인, 소스 코드에 대한
저작권은 Opkle에게 있습니다.
img default description