17장. 이미지/DIMS

이 장에서는 이미지를 전송시점에 on-the-fly로 변환/전송하는 DIMS(딤스)에 대해 다룬다. DIMS(Dynamic Image Management System)는 원본이미지를 다양한 형태로 가공하는 기능이다.

../_images/dims.png

다양한 동적 이미지 가공

이미지는 동적으로 생성되며 원본 이미지 URL뒤에 약속된 키워드와 가공옵션을 붙여서 호출한다. 가공된 이미지는 캐싱되어 원본서버 이미지가 바뀌지 않는 이상 다시 가공되지 않는다.

예를 들어 원본 파일이 /img.jpg라면 다음과 같은 형식으로 이미지를 가공할 수 있다. ("12AB"는 약속된 Keyword이다.)

http://image.example.com/img.jpg    // 원본 이미지
http://image.example.com/img.jpg/12AB/optimize
http://image.example.com/img.jpg/12AB/resize/500x500/
http://image.example.com/img.jpg/12AB/crop/400x400/
http://image.example.com/img.jpg/12AB/composite/watermark1/

<Dims> 는 별도로 설정하지 않으면 모두 비활성화되어 있다.

# server.xml - <Server><VHostDefault><Options>
# vhosts.xml - <Vhosts><Vhost><Options>

<Dims Status="Active" Keyword="dims" MaxSourceSize="10" OnFailure="message" />
  • <Dims>
    • Status DIMS활성화 ( Active 또는 Inactive )
    • Keyword 원본과 DIMS를 구분하는 키워드
    • MaxSourceSize (기본: 10MB) 변환을 허용할 최대 원본 이미지 크기 (단위: MB)
    • OnFailure 이미지 변환실패 시 동작방식
      • message (기본) 500 Internal Error로 응답한다. 본문에는 구체적인 실패 이유를 명시한다.
        • The original file was not successfully downloaded. 원본이미지를 완전하게 다운로드 하지 못했다.
        • The original file size is too large. 원본이미지 크기가 MaxSourceSize 를 넘어 변환하지 못했다.
        • The original file loading failed. 원본 이미지 데이터를 불러오지 못했다.
        • Image converting failed or invalid DIMS command. 잘못된 명령어 또는 지원되지 않는 이미지등으로 인해 변환하지 못했다.
      • redirect 원본 이미지 주소로 302 Redirect한다.

최적화

최적화란 이미지 품질을 저하시키지 않으면서 이미지를 압축하는 과정이다. JPEG, JPEG-2000, Loseless-JPEG 이미지만 지원이 가능하다. 이미 다른 도구등을 통해 최적화된 이미지는 더 이상 최적화되지 않는다.

http://image.example.com/img.jpg/dims/optimize

최적화는 키워드 이외 별도의 옵션을 가지지 않는다. 그러므로 다른 변환조건과 조합할 때 맨 뒤에 명시하는 편이 바람직하다.

http://image.example.com/img.jpg/dims/resize/100x100/optimize

다른 모든 DIMS기능이 시스템 자원을 많이 사용하지만 그 중에서도 최적화가 가장 무거운 작업이다. 다음은 HitRatio가 0%인 상태에서 이미지 크기별 성능 테스트 결과이다.

크기 처리량 응답속도(ms) 클라이언트 트래픽(Mbps) 원본 트래픽(Mbps) 트래픽 절감률(%)
16KB 720 19.32 46.32 92.62 49.99
32KB 680 20.68 86.42 165.08 47.65
64KB 285 50.16 80.67 150.96 46.56
128KB 274 57.80 164.35 276.52 40.56
256KB 210 80.74 99.42 432.35 77.00
512KB 113 156.18 160.54 436.04 63.18
1MB 20 981.07 90.62 179.88 49.62

약 50%내외의 트래픽 절감률이 있으므로 매우 효과적이다. 다시 한번 말하지만 최적화는 매우 무거운 작업이다. 표를 통해 알 수 있듯이 이미지 크기가 가장 큰 변수가 된다.

때문에 충분한 고려없이 서비스에 적용했다가는 큰 낭패를 볼 수 있다. 적당한 Request hit ratio 가 있는 상황이 바람직하나, 그렇지 않다면 서비스 규모에 맞게 물리적인 CPU자원을 충분히 확보해야 한다.

주석

메타정보만을 삭제하고 싶을 경우 아래 명령어를 이용한다.

http://image.example.com/img.jpg/dims/strip/true

Annotation [Enterprise]

Annotation은 이미지에 글씨를 입힐 수 있는 기능이다.

../_images/dims_annotation.png

사전에 제작된 텍스트 이미지를 "합성" 하는 것이 아니라, 다양한 펜(폰트, 색상, 위치 등)을 이용해 이미지에 텍스트를 타이핑 한다.

# server.xml - <Server><VHostDefault><Options>
# vhosts.xml - <Vhosts><Vhost><Options>

<Dims Status="Active" Keyword="dims">
   <Annotation Name="maintext"> ... </Annotation>
   <Annotation Name="subtext"> ... </Annotation>
   <Annotation Name="watermark"> ... </Annotation>
</Dims>

각각의 <Annotation> 은 고유한 Name 으로 명명된다. 여러 <Annotation> 을 미리 등록하고 다음과 같이 * 를 구분자로 조합하여 사용한다.

// 메인 텍스트
http:// ... /dims/annotation/maintext

// 메인 텍스트 + 서브 텍스트
http:// ... /dims/annotation/maintext*subtext

// 서브 텍스트 + 워터마크
http:// ... /dims/annotation/subtext*watermark

// 메인 텍스트 + 서브 텍스트 + 워터마크
http:// ... /dims/annotation/maintext*subtext*watermark

기본 텍스트는 <Annotation> 의 값이며, 약속된 QueryString 을 통해 텍스트를 입력받을 수 있다.

# server.xml - <Server><VHostDefault><Options><Dims>
# vhosts.xml - <Vhosts><Vhost><Options><Dims>

<Annotation Name="statictext">STON Edge Server</Annotation>
<Annotation Name="maintext">$QUERYSTRING[msg]</Annotation>
<Annotation Name="subtext">$QUERYSTRING[tag]</Annotation>

다음과 같이 텍스트를 전달한다.

// "STON Edge Server" 를 statictext로 삽입
http:// .../dims/annotation/statictext

// msg(="HelloWorld") 를 maintext로 삽입
http:// ...?msg=HelloWorld/dims/annotation/maintext

// msg(="HelloWorld") 를 maintext로, tag(="Event")를 subtext로 삽입
http:// ...?msg=HelloWorld&tag=Event/dims/annotation/maintext*subtext

// tag(="Event")를 subtext로 삽입 (maintext 누락)
http:// ...?msg=HelloWorld&tag=Event/dims/annotation/subtext

멀티라인을 입력하려면 라인피드(line feed) 문자인 \n 을 입력한다.

http:// ...?msg=1st\n2nd/dims/annotation/maintext

주석

공백 등 표준 URL escape 문자는 브라우저가 적절히 인코딩하지만 한글의 경우 브라우저마다 처리 방식이 달라 깨질 수 있다. 따라서 한글을 입력할 경우 서버에서 한글 인코딩된 결과를 제공해야 한다.

<Annotation> 은 다양한 속성을 지원한다.

속성 기본 값 설명
Name (없음) <Annotation> 이름
Font none (System Font) 폰트를 지정한다. (ttf, otf, woff 지원)
FontSize 10 텍스트 크기
FontColor #000000 텍스트 색상
TextAlign center 텍스트 정렬 ( left , center , right )
BackgroundColor none (투명) 배경 색상
BackgroundWidth (텍스트 크기에 맞춤) 배경 폭
BackgroundHeight (텍스트 크기에 맞춤) 배경 높이
Gravity c 텍스트 위치 기준
Geometry +0+0 Gravity로부터 거리
Dissolve 50 텍스트 투명도
  • FontColorBackgroundColor 는 RGB의 16진수 표현(#FF0000)으로 설정한다.
  • BackgroundWidthBackgroundHeight 값이 0이면 텍스트에 맞추어진다. Origin 을 지정할 경우 대상 이미지의 폭과 넓이를 사용한다.
  • Gravity , Geometry , Dissolve 는 <합성>과 동일하다.

Font

폰트 라이선스 문제로 인해 아래 폰트들(.ttf)만을 기본 배포한다.

Font License
EB Garamond Open Font License
Lato Open Font License
Montserrat Open Font License
Open Sans Open Font License
Oswald Open Font License
Raleway Open Font License
Source Sans Pro Open Font License
Roboto Apache License, Version 2.0
나눔고딕 Open Font License
나눔명조 Open Font License
서울한강 영리/비영리무료
서울남산 영리/비영리무료

폰트는 다음 경로에 복사하여 추가가 가능하다.

/usr/local/ston/fonts

설정백업 시 용량 문제로 사용자가 추가한 폰트만을 백업한다.

주석

보안적인 이유로 Web Management를 통한 업로드는 지원하지 않는다.

잘라내기

좌상단을 기준으로 원하는 영역만큼 이미지를 잘라낸다. 영역은 width x height{+-}x{+-}y{%} 로 표현한다. 다음은 좌상단 x=20, y=30을 기준으로 width=100, height=200만큼 잘라내는 예제다.

http://image.example.com/img.jpg/dims/crop/100x200+20+30/

이미지 중앙을 기준으로 하고 싶은 경우 cropcenter명령어를 사용한다.

http://image.example.com/img.jpg/dims/cropcenter/100x200+20+30/

Thumbnail 생성

Thumbnail 을 생성한다. 크기와 옵션은 width x height{%} {@} {!} {<} {>} 로 표현한다. 기본적으로 이미지의 가로와 세로는 최대값을 사용한다. 이미지를 확대 또는 축소하여도 가로 세로 비율은 유지된다. 정확하게 지정한 크기로 이미지를 조절할 때는 크기 뒤에 느낌표(!)를 추가한다. 640X480! 라는 표현은 정확하게 640x480 크기의 Thumbnail을 생성한다는 뜻이다. 만약 가로 또는 세로 크기만 지정된 경우, 생략된 값은 가로/세로 비율에 의해 자동결정 된다.

예를 들어 /thumbnail/100/ 은 가로 크기에 맞추어 세로 크기가 결정되며 /thumbnail/x200/ 은 세로 크기에 맞추어 가로 크기가 결정된다. 가로/세로 크기를 이미지의 크기에 맞추어 백분율(%)로 표현할 수 있다. 이미지 크기를 늘리려면, 100 보다 큰 값(예 : 125 %)을 사용한다. 이미지 크기를 줄이려면 100 미만의 비율을 사용한다. URL Encoding규칙에 따라 %문자가 %25로 인코딩 됨을 명심해야 한다.

예를 들어 50%라는 표현은 50%25로 인코딩 된다. 다음은 width=78, height=110크기의 Thumbnail을 생성하는 예제다.

http://image.example.com/img.jpg/dims/thumbnail/78x110/

Resizing

이미지 크기를 변경한다. 크기는 width x height 로 표현한다. 이미지는 변경되어도 비율은 유지된다. 다음은 원본 이미지를 width=200, height=200크기로 변경하는 예제다.

http://image.example.com/img.jpg/dims/resize/200x200/

그 외 명령어는 다음과 같다.

  • resizec - 축소하면 resize와 동일하지만, 확대하면 이미지는 유지되고 캔버스 크기만 확대된다.
  • extent - 캔버스만 조절하는 명령어. 축소하면 crop과 동일한 효과를 내지만, 확대하면 resizec와 동일하게 확대된다.
  • trim - 상하좌우 흰색배경을 제거한다.

Format 변경

이미지 포맷을 변경한다. 지원되는 포맷은 png , jpg , gif , webp 이다. 다음은 JPG를 PNG로 변환하는 예제다.

http://image.example.com/img.jpg/dims/format/png/

주석

[Enterprise] v18.06.0 부터 WebP를 지원한다.

품질 변경

이미지 품질을 조절한다. 이 기능은 전송되는 이미지 용량을 줄일 수 있어서 효과적이다. 유효 범위는 0부터 100까지다. 다음은 이미지 품질을 25%로 조절하는 예제다.

http://image.example.com/img.jpg/dims/quality/25/

이펙트

이미지에 다양한 이펙트를 줄 수 있다.

설명 명령어 변수
반전 invert true 또는 false
그레이 스케일 grayscale true 또는 false
대칭이동 flipflop vertical 또는 horizontal
밝기조절 bright 0 ~ 100
회전 rotate 0 ~ 360 (도)
세피아 sepia 0 ~ 1
모서리 라운드 round 0 ~ 90

합성

두 이미지를 합성한다. 앞서 설명한 기능과는 다르게 합성조건은 미리 설정되어 있어야 한다. 주로 워터마크 효과를 내기 위해 사용된다.

# server.xml - <Server><VHostDefault><Options>
# vhosts.xml - <Vhosts><Vhost><Options>

<Dims Status="Active" Keyword="dims" port="8500">
   <Composite Name="water1" File="/img/small.jpg" />
   <Composite Name="water2" File="/img/medium.jpg" Gravity="se" Geometry="+0+0" Dissolve="50" />
   <Composite Name="water_ratio" File="/img/wmark_s.png" Gravity="s" Geometry="+0+15%" Dissolve="100" />
</Dims>
  • <Composite>

    이미지 합성조건을 설정한다. 속성에 의해 정해지며 별도의 값을 가지지 않는다.

    • Name 호출될 이름을 지정한다. '/'문자는 입력할 수 없다. URL의 "/composite/" 뒤에 위치한다.

    • File 합성할 이미지파일 경로를 지정한다.

    • Gravity (기본: c) 합성할 위치는 좌측상단부터 9가지의 포인트(nw, n, ne, w, c, e, sw, s, se)가 존재한다.

      ../_images/conf_dims2.png

      Gavity 기준점

    • Geometry (기본: +0+0) Gravity 기준으로 합성할 이미지 위치를 설정한다. {+-}x{+-}y. 붉은색 원은 Gravity속성에 따라 +0+0이 의미하는 기준점으로 +x+y의 값이 커질수록 이미지 안쪽으로 배치된다. 초록색 화살표는 +x, 보라색 화살표는 +y가 증가하는 방향이다. -x-y를 사용하면 대상 이미지의 바깥에 위치하게 되어 결과 이미지에서는 보여지지 않는다. 이 속성은 다소 복잡해 보이지만 이미지 크기를 자동으로 계산하여 배치하므로 일관된 결과물을 얻을 수 있어서 효과적이다. 또한 +x%+y% 처럼 %옵션을 주어 비율로 배치할 수도 있다.

    • Dissolve (기본: 50) 합성할 이미지의 투명도(0~100).

<Composite> 을 설정했다면 Name 속성을 사용하여 이미지를 합성할 수 있다.

http://image.example.com/img.jpg/dims/composite/water1/

원본이미지 조건판단

원본 이미지 조건에 따라 동적으로 가공 옵션을 다르게 적용할 수 있다. 예를 들어 1024 X 768 이하의 이미지는 품질을 50%로 떨어트리고 그 이상의 이미지는 1024 X 768로 크기변환을 하려면 다음과 같이 <ByOriginal> 을 설정한다.

# server.xml - <Server><VHostDefault><Options>
# vhosts.xml - <Vhosts><Vhost><Options>

<Dims Status="Active" Keyword="dims" port="8500">
   <ByOriginal Name="size1">
      <Condition Width="1024" Height="768">/quality/50/</Condition>
      <Condition>/resize/1024x768/</Condition>
   </ByOriginal>
</Dims>
  • <ByOriginal> Name 속성으로 호출한다. 하위에 다양한 조건의 <Condition> 을 설정한다.

  • <Condition> 조건에 만족하는 경우 설정된 변환을 수행한다.

    • Width 가로 길이가 설정 값보다 작으면 적용된다.
    • Height 세로 길이가 설정 값보다 작으면 적용된다.

    조건을 설정하지 않으면 원본 이미지 크기에 상관없이 변환된다.

<Condition> 은 명시된 순서대로 적용된다. 그러므로 작은 이미지 조건을 먼저 배치해야 한다. 다음과 같이 호출한다.

http://image.example.com/img.jpg/dims/byoriginal/size1/

또 다른 예로 이미지 크기에 따라 다른 <Composite> 조건을 줄 수 있다. 이런 경우 다음과 같이 사전에 정의된 <Composite>Name 으로 설정한다.

# server.xml - <Server><VHostDefault><Options>
# vhosts.xml - <Vhosts><Vhost><Options>

<Dims Status="Active" Keyword="dims" port="8500">
   <Composite Name="water1" File="/img/small.jpg" />
   <Composite Name="water2" File="/img/medium.jpg" Gravity="se" Geometry="+0+0" Dissolve="50" />
   <Composite Name="water3" File="/img/big.jpg" Gravity="se" Geometry="+10+10" Dissolve="50" />
   <ByOriginal Name="size_water">
      <Condition Width="400">/composite/water1/</Condition>
      <Condition Width="800">/composite/water2/</Condition>
      <Condition>/composite/water3/</Condition>
   </ByOriginal>
</Dims>

다음과 같이 호출하면 원본 이미지 크기에 따라 합성이 적용된다.

http://image.example.com/img.jpg/dims/byoriginal/size_water/

Animated GIF

Animated GIF에 대해서도 모든 DIMS변환이 동일하게 적용된다. 처리 순서는 다음과 같다.

  1. Animated GIF를 낱개의 이미지들로 분해한다.
  2. 각각의 이미지를 변환한다.
  3. 변환된 이미지를 Animated GIF로 결합한다.

결합된 이미지가 많을수록 처리비용이 높아 서비스 품질이 저하될 수 있다. 이런 경우 첫 번째 이미지에 대해서만 변환하도록 설정하면 처리 비용을 낮출 수 있다.

# server.xml - <Server><VHostDefault><Options>
# vhosts.xml - <Vhosts><Vhost><Options>

<Dims FirstFrameOnly="OFF" />
  • FirstFrameOnly (기본: OFF) ON인 경우 Animated GIF의 첫 장면만 변환한다.

다음과 같이 URL을 호출할 때 FirstFrameOnly 옵션을 명시적으로 지정할 수 있다.

http://image.example.com/img.jpg/dims/firstframeonly/on/resize/200x200/
http://image.example.com/img.jpg/dims/firstframeonly/off/resize/200x200/

위와 같이 URL에 명시적으로 지정되어 있는 경우 설정보다 우선한다.

주석

limit 명령어를 통해 Animated GIF의 프레임 수를 조절할 수 있다.

http://image.example.com/img.jpg/dims/limit/3
http://image.example.com/img.jpg/dims/limit/3/resize/200x200

기타

이상의 기본기능을 결합하여 복합적인 이미지 가공을 할 수 있다. 예를 들어 Thumbnail생성(78x110), 포맷을 JPG에서 PNG로 변환, 품질 50% 이상의 옵션을 한번의 호출로 실행할 수 있다.

http://image.example.com/img.jpg/dims/thumbnail/78x110/format/png/quality/50/

DIMS는 URL을 이용하여 이미지 가공이 이루어진다. 그러므로 URL에 영향을 주는 다른 옵션들 때문에 원하지 않는 결과가 얻어지지 않도록 주의해야 한다.

  • QueryString 구분OFF 라면 키워드 이전의 QueryString이 무시된다.

    http://image.example.com/img.jpg?session=5234&type=37/dims/resize/200x200/
    

    위와 같은 호출에 이 설정이 ON 이라면 입력된 URL 그대로 인식되지만 OFF라면 다음과 같이 인식된다.

    http://image.example.com/img.jpg/dims/resize/200x200/
    
  • 대소문자 구분OFF 라면 모든 URL을 소문자로 변환하여 처리한다. 그러므로 DIMS 키워드에 대문자가 포함되었다면 키워드를 인식하지 못한다. 항상 키워드는 소문자로 사용하는 것이 좋다.