Adobe ColdFusion(https://www.adobe.com/kr/products/coldfusion-family.html)의 경우 CFML를 작성하여 동적으로 PDF를 생성할때 필요한 서체를 사전에 Administrator환경에서 서버상의 Font 목록을 조회하고 등록하여 쓸 수 있는 있t습니다. 물론 서체가 없는 경우 기본 시스템서체를 활용하여 보여줍니다만 한글서체가 서버에 설치되어 있지 않다면 제대로 표현이 불가능합니다.  마찬가지로 Railo(현재는 Lucee로 Fork)에서도 비슷한 기능을 관리자 기능에서 제공했으나 Lucee(https://lucee.org)에서는 해당기능이 빠져버렸고 PDF엔진 자체도 최근 변경이 되어 fonts.jar 파일에 폰트를 추가하는 방법으로는 해결이 안됩니다. (Lucee 5.3 이후)

 

<ColdFusion으로 동적 생성한 다양한 서체로 표현된 PDF문서 - 나눔서체> 

Lucee(https://docs.lucee.org/guides/cookbooks/Flyingsaucer.html) 5.3 이후 버전에서는 Flying Saucer PDF엔진(https://github.com/flyingsaucerproject/flyingsaucer)을 사용하게됨으로서 PDF내에서 한글폰트 및 여러 오픈소스 또는 구매한 상용폰트를 서버의 관리자 사전 등록하지 않고도 사용할 수 있게 되었습니다. 이전의 경우 위에서 설명드렸듯이 서버상에 ttf 등의 서체를 저장하고 Adobe ColdFusion Server나 Lucee(구버전)의 Administrator 환경에서 해당 ttf가 있는 Path를 사전에 등록해 줘야 했지만 Flying Saucer PDF Engine에서는 그럴 필요없이 웹개발자가 웹루트에 ttf를 업로드해놓고 마치 HTML의 CSS의 font-face처럼 동적으로 로드할 수 있게 되었습니다. (대부분 서버관리자와 개발자는 별개이고 서버관리자는 절대로 개발자에게 ColdFusion Administrator 접속권한을 주지 않을테니까 이는 매우 중요합니다.)

 

웹루트 경로에 fonts라는 디렉토리를 만들고 그 안에 사용할 ttf 서체들을 올리시면 됩니다. 아래처럼요.

위 그림의 fonts 디렉토리 안에는 아래의 서체파일들이 들어 있습니다.

Java나 PHP 또는 ColdFusion과 같은 동적 웹언에서는 PDF를 생성한다는 것이 대개는 Database에서 불러온 데이터를 HTML로 표현하고 이를 다시 PDF로 변환하는 과정이기 때문에 실제로 CSS의 버전이나 지원하는 HTML의 버전이 최근의 것을 지원할 수록 더 좋은 엔진이다 할 수 있습니다. (CSS의 다양한 기법을 더 활용할 수 있어 정교한 PDF를 생성할 수 있으니까요)

 

가령 PHP의 TCPDF(https://tcpdf.org/)의 경우 HTML Table태그내의 td 영역내에서 세로정렬을 지원하지 않습니다. 따라서 예쁜? PDF문서를 렌더링하기 어렵습니다.  

 

각설하고, 본론으로 돌아와서 이야기하자면 ColdFusion에서는 <cfdocument></cfdocument> 태그 사이에 HTML 코드를 넣으면 그 코드를 PDF로 자동변환하여 출력해줍니다. 다음의 코드에서 CSS부분의 @font-face과 같이 각 폰트마다 선언해주고 사용하시면 됩니다. 첨부파일(PDF)을 보시면 아래 코드가 실행된 결과가 보입니다. <cfdocument>의 속성으로 코드에 나타는 부분 marginbottom 등은 Adobe ColdFusion의 Tag 설명서(https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-d-e/cfdocument.html)를 보시면 쉽게 알 수 있습니다. 단 영어.

 

test.pdf
0.14MB

<cfprocessingdirective pageencoding="utf-8">

<cfcontent type="application/pdf">
<cfheader name="Content-Disposition" value="inline; filename=./test.pdf">
<cfdocument format="PDF" marginbottom="1" marginleft="1" marginright="1" margintop="1" orientation="portrait" overwrite="true" unit="cm" localurl="true" fontembed="true" pagetype="a4">

<!-- 실제로 PDF에 나타날 HTML블럭 시작 -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
	<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			<!--
				@font-face {
					font-family: NanumGothic;              /* 폰트이름지정 */
					src: url('./fonts/NanumGothic.ttf');   /* 폰트경로지정 */
					-fs-pdf-font-embed: embed;
					-fs-pdf-font-encoding: Identity-H;
				}
				@font-face {
					font-family: NanumMyeongjo;              /* 폰트이름지정 */
					src: url('./fonts/NanumMyeongjo.ttf');   /* 폰트경로지정 */
					-fs-pdf-font-embed: embed;
					-fs-pdf-font-encoding: Identity-H;
				}
				@font-face {
					font-family: NanumPen;              /* 폰트이름지정 */
					src: url('./fonts/NanumPen.ttf');   /* 폰트경로지정 */
					-fs-pdf-font-embed: embed;
					-fs-pdf-font-encoding: Identity-H;
				}
				@font-face {
					font-family: NanumBarunGothic;              /* 폰트이름지정 */
					src: url('./fonts/NanumBarunGothic.ttf');   /* 폰트경로지정 */
					-fs-pdf-font-embed: embed;
					-fs-pdf-font-encoding: Identity-H;
				}
				
				body {
					width: 100%;
					font-size: 16px;
					color:#000;
					line-height:180%;
				}	

				img {border: 0 none;}

				table {
					width: 100%;
					border: 0.5px solid #000;
					border-collapse: collapse;
				}
				th, td {
					border: 0.5px solid #000;
					padding: 10px;
					word-break:break-all;
				}
				
				.nm { font-family: 'NanumMyeongjo', serif; }   /* 지정폰트명 */
				.ng { font-family: 'NanumGothic', serif; }     /* 지정폰트명 */
				.np { font-family: 'NanumPen', serif; }     /* 지정폰트명 */
				.nb { font-family: 'NanumBarunGothic', serif; }     /* 지정폰트명 */
				
			-->
		</style>
	 </head>

	<body>
	<table>
		<tr>
			<td align="center" class="nm">
			나랏말싸미 듕귁에 달아 문짜와로 서르사맛디 아니할쎼<br>
			내 이런 젼차로 어린 뷕셩이 니르고져 홇베이셔도<br>
			마참내 제 쁫을 시러펴디 몯핧노미 하노라<br>
			내 이를 위하야 어엿비너겨 새로 스물여듧짜랄 맹가노니<br>
			해야 수비니겨 날로 쑤매 뻔한킈 하고져 할 따라미니라<br>
			</td>
		</tr>
		<tr>
			<td align="center" class="ng">
			나랏말싸미 듕귁에 달아 문짜와로 서르사맛디 아니할쎼<br>
			내 이런 젼차로 어린 뷕셩이 니르고져 홇베이셔도<br>
			마참내 제 쁫을 시러펴디 몯핧노미 하노라<br>
			내 이를 위하야 어엿비너겨 새로 스물여듧짜랄 맹가노니<br>
			해야 수비니겨 날로 쑤매 뻔한킈 하고져 할 따라미니라<br>
			</td>
		</tr>
		<tr>
			<td align="center" class="np">
			나랏말싸미 듕귁에 달아 문짜와로 서르사맛디 아니할쎼<br>
			내 이런 젼차로 어린 뷕셩이 니르고져 홇베이셔도<br>
			마참내 제 쁫을 시러펴디 몯핧노미 하노라<br>
			내 이를 위하야 어엿비너겨 새로 스물여듧짜랄 맹가노니<br>
			해야 수비니겨 날로 쑤매 뻔한킈 하고져 할 따라미니라<br>
			</td>
		</tr>
		<tr>
			<td align="center" class="nb">
			나랏말싸미 듕귁에 달아 문짜와로 서르사맛디 아니할쎼<br>
			내 이런 젼차로 어린 뷕셩이 니르고져 홇베이셔도<br>
			마참내 제 쁫을 시러펴디 몯핧노미 하노라<br>
			내 이를 위하야 어엿비너겨 새로 스물여듧짜랄 맹가노니<br>
			해야 수비니겨 날로 쑤매 뻔한킈 하고져 할 따라미니라<br>
			</td>
		</tr>			
	</table>
	</body>
</html>
<!-- 실제로 PDF에 나타날 HTML블럭 끝 -->

</cfdocument>
Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

Lucee ColdFusion Server를 설치 후 CGI.REMOTE_ADDR 변수의 반환 값이 항상 127.0.0.1로만 반환 될 때 Lucee가 Deploy된 Tomcat의 web.xml에 아래의 코드를 추가해야 합니다. 

 

Lucee는 Tomcat에 deploy된 상태로 배포되기 때문에 별도의 Tomcat 등의 서버에 웹아카이브(WAR)로 배포하지 않았다면 기본 설치경로 예를 들어 Linux의 경우는 /opt/lucee/tomcat 이 됩니다. /opt/lucee/tomcat/conf/web.xml를 편집기로 열고 맨 마지막 </web-app>  노드 바로 위에 아래의 코드를 추가하시고 Lucee를 재시작하면 됩니다. 

 

<filter>
	<filter-name>RemoteIpFilter</filter-name>
	<filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>RemoteIpFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

일주일안에 배워보는 ColdFusion 러닝사이트입니다. 예전 블로그에서도 소개한바 있는데요. 기초중의 기초를 배울 수 있습니다. 영어사이트라 약간의 영어를 봐야 하지만 어렵지 않으리라 봅니다. 어려울까요? ㅋ

 

저 역시 손놓은지 7년이 넘어가서 다시 봐야겠네요. ㅠㅠ 

 

http://learncfinaweek.com/

Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

기존에 JSP나 Servlet으로 구축된 어떤 시스템이 있다고 가정해 봅시다. 이 시스템은 워낙 방대하고 복잡해서 전면적인 개편이나 신규구축이 어렵다고 합니다. 그런데 ColdFusion의 강력한 기능과 효율적인 개발환경을 도입하고자 합니다. 상이한 두 환경과 이질적인 웹어플리케이션간의 통합을 어떻게 해야 할까요?

어려 방법이 있겠습니다만, 가장 우선 두 이질적인 웹어플리케이션을 통합하는데 있어 중요한 것은 데이터베이스와 파일의 기본적인 환경 공유와 함께 사용자인증, 어플리케이션 인증 등을 위해 세션(Session)을 공유해야 정확한 의미에서 "통합"이라는 단어를 쓸 수 있겠죠.

가령, PHP로 구축된 어떤 어플리케이션과 ASP로 구축된 어떤 어플리케이션을 단일호스트(웹사이트환경)에서 모두 실행 할 수는 있겠죠. Windows 시스템에 IIS웹서버를 사용하고, PHP를 별도로 추가 설치한 후 IIS에 매핑을 해주면 되니까요. 두 언어 모두 MSSQL이나 Oracle 또는 MySQL에 접근하는 방식만 다를 뿐 기존 데이터베이스를 얼마든지 이용가능합니다.

그런데 ASP로 구축된 회원로그인과 같은 기능은 PHP와 통합하기 어렵죠. ASP에서 인증을 처리하고 세션을 생성해도 PHP에서는 가져다 쓸 수 없으니까요. 약간의 꼼수로 ASP세션을 파일이나, DB에 넣어두고 PHP가 그때 참조하도록 조치할 수 있겠지만 복합한 비지니스 환경에서 보안적 위협을 무릎쓰고 그렇게 할 수 는 없는 노릇입니다.

반대로 PHP와 ColdFusion, 또는 PHP와 JSP는 어떨까요? 한가지 확실한건 Resin을 만든 Caucho의 Quercus와 같은 Java로 만들어진 PHP해석기 때문에 PHP로 만들어진 웹어플리케이션이 Quercus에서 구동이 보장되는 호환성만 갖추었다면 J2EE기반의 어떤 언어 가령 JSP나 ColdFusion과는 Session 데이터를 서로 공유할 수 있습니다. 물론 ColdFusion 중 BlueDragon이란 .NET기반으로 만들어진 WAS을 사용한다면 닷넷과도 공유는 가능할 지 모르겠습니다.

이질적인 언어들간의 세션공유는 보안적측면을 떠나서 메모리상에서만 존재하는 데이터에 대한 상호공유의 문제이므로 완벽한 통합과 관련이 깊다고 생각됩니다. 오늘은 JSP와 ColdFusion사이에 어떻게 세션을 공유하는지 알아보려 합니다.

ColdFusion은 잘 아시다시피 J2EE기반으로 만들어진 WAS입니다. Tomcat과 같은 Servlet부터, Resin, Glashfish, Jboss, Weblogic 등등 대부분의 J2EE기반의 WAS에 통합되어 사용됩니다. 따라서 설정의 문제이지 기본적으로 JSP를 구동할 수 있는 환경이 자동으로 조성됩니다.

문제는 단독으로 JSP나 또는 ColdFusion을 사용하는 경우라면 문제가 없겠지만 기존의 JSP/Servlet기반의 어플리케이션에 ColdFusion을 도입하려 한다면 기존의 J2EE WAS에 단순히 ColdFusion의 여러 제품들 가령, Adobe ColdFusion이나 Railo, Open BlueDragon 같은 것을 Deploy하기만 하면 됩니다.

문제는 ColdFusion을 Deploy하여 JSP와 ColdFusion을 단일 J2EE WAS내에서 구동되게 하였어도 Session이 통합되지 않으면 로그인 인증처리 등등에서 문제가 생길 수 있습니다. 

자, 먼저 JSP에서 세션을 생성하고 그 세션을 ColdFusion에서 가져와 봅시다. JSP세션이 살아있는 동안은 ColdFusion에서 참조할 수 있지요. 여기서 몇가지, 우선 JSP세션을 가져와서 단순히 이용만할 것인지(JSP세션이 사라지면 ColdFusion에서도 참조할 수 없음) 아니면 JSP세션을 가져와서 ColdFusion내에서 세션을 한번 더 생성하여 JSP세션의 존재유무와 상관없이 ColdFusion에서는 생성된 세션을 계속 이용하는지의 여부를 생각할 수 있습니다. 어떠한 경우든 상관없이 문제는 생성되는 JSP세션을 ColdFusion이 정확하게 인식해야 한다는 것 입니다.

 

JSP에서의 세션이 아직 생성되지 않은 화면을 ColdFusion화면(새창)에서 조회해 본 화면
JSP에서 생성한 세션데이터 "JSP가 만든 세션"을 ColdFusion화면에서 가져온 화면

이제 간단한 소스를 살펴보죠. JSP에서는 3가지로 간단하게 세션을 조회하는 index.jsp파일과 세션을 생성하는 create_session.jsp과 세션을 제거하는 remove_session.jsp입니다. 매우 간단한 코드입니다. 그리고 ColdFusion에서는 메모리 영역에 존재하는 세션데이터를 가져오는 코드입니다.  

 

index.jsp

<%@page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>

<% 
	String myStr;
	if(session.getAttribute("myVar")!=null) {
		myStr=(String)session.getAttribute("myVar");
	} else {
		myStr="세션값 없음";
	}
%>

세션값 : <%=myStr%>

create_session.jsp

<%@page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<% session.setAttribute("myVar", "JSP가 만든 세션");%>

remove_session.jsp

<%@page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<% session.invalidate();%>

get_session.cfm

<!--- Java객체생성 --->
<cfset htreq=createObject("java","javax.servlet.http.HttpServletRequest")>
<cfset htsess=createObject("java","javax.servlet.http.HttpSession")>
<cfset jumap=createObject("java","java.util.Map")>

<!--- 세션가져오기 --->
<cfset htreq = GetPageContext().getRequest()>
<cfset htsess = htreq.getSession()>
<cfset jumap = htsess.getAttribute("myVar")>

<!---세션값 출력 --->
<cfoutput>#jumap#</cfoutput>

 

ColdFusion 서버들은 Java기반의 여러 응용서버위에 적용되므로 이렇게 완벽하게 세션데이터를 공유할 수 있습니다. 고로 기존에 JSP나 Java로 짜여진 시스템이 있더라도 ColdFusion으로 확장이 가능하답니다.

Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

ColdFusion에서는 오래전부터  태그를 지원하기 전부터 Java를 응용하여 만들어진 많은 Captcha 소스가 있었습니다. 대부분 쉽게 가져다 쓸 수 있도록 CFC(콜드퓨전컴포넌트)나 UDF(사용자정의 함수) 또는 Custom Tag(사용자정의 태그)로 제공되고 있어서 매우 쉽게 쓸 수 있었습니다.

그러나 Adobe ColdFusion이란 이름으로 Adobe에 인수되어 첫 출시된 버전 8에서는 쉽게 이미지를 핸들링 할 수 있는 태그를 지원하면서 Captcha도 동시에 쉽게 구현되도록 지원되기 시작했습니다. 이후 Railo나 OpenBD도 모두 지원하기 시작했습니다. 따라서 대부분의 ColdFusion서버에는 다음과 같은 단 한줄의 태그로 이미지로 만들어지는 Captcha를 생성합니다.

 

<cfimage action="captcha" difficulty="high" fontSize="25" width="300" height="50" text="Captcha" fonts="Arial" />

Adobe ColdFusion에서 생성한 Captcha 이미지

물론, Adobe ColdFusion 10 이상의 버전에서는 ImageCreateCaptcha()란 함수도 지원하여 또다른 코드방식으로 생성할 수 있습니다만 이 역시 단 2줄이면 Captcha의 생성은 끝납니다. ColdFusion 언어의 특성상 HTML의 태그와 유사하여 Captcha를 생성하더라도 간단한 속성의 정의로 가로세로의 크기, 서체, 서체의 크기, Captcha의 난이도를 변경할 수 있습니다. 한국인을 위한 전용 웹사이트라면 한글서체를 이용해서 한글 Captcha도 만들 수 있습니다. (단 한줄의 코드로) 

<cfset funcCaptcha = ImageCreateCaptcha(50, 300, "Captcha", "medium", "serif,sansserif", "25") />
<cfimage action="writetoBrowser" source="#funcCaptcha#" />

위 Tag로 생성한 경우나 함수로 생성한 경우나 개발자가 신경 쓸 부분은 Captcha의 내용 즉 문자를 랜덤으로 조합하여 보여줄 것만 고민하면 됩니다. 아래의 예제에서 문자나 숫자범위에서 랜덤하게 추출하는 것은 코드로 살펴보도록 하겠습니다.

자, 말을 좀 바꾸어서, 그런데 Captcha를 쓰는 가장 큰 이유는 스팸과 같은 자동등록되는 Bot에 의한 게시글 공격을 막기 위함인데 Captcha의 특성이 웹 접근성과 완전히 위배된다는 것에서 괴리가 생깁니다. 웹 접근성적인 관점에서는 모든 이미지는 해당 이미지의 내용과 특성을 알 수 있도록 HTML의 태그의 alt 속성과 같은 기능을 이용해서 시각장애인이 쉽게 인지할 수 있도록 구현해야 합니다. (백남중님의 Captcha에 대한 단상 참조)

그런데, 웹 접근성 관점에서 Captcha를 읽을 수 있게 만든다고 HTML소스코드내에 Captcha의 내용을 텍스트로 제공하면 Captcha를 쓰는 이유가 사라지게 됩니다. 바로 Bot 들이 인지할 수 있기 때문이죠. Captcha 자체의 이미지도 OCR같은 기능을 이용하면 쉽게 텍스트를 탐지할 수 있는데 말이죠.

 

Google의 ReCaptcha

그래서 구글의 reCaptcha 같은 프로젝트에서는 시각장애인을 위하여 Captcha의 문자를 소리로 들려주기도 합니다. 해외에서 제공되는 대부분의 Captcha관련 API는 음성을 지원하여 간단하게 가져다 쓸 수 있지만(물론 ColdFusion에서도 이용할 수 있도록 ColdFusion reCAPTCHA tag와 같은 오픈소스 프로젝트도 있습니다.) reCaptcha의 소리를 들어보시면 알겠지만 이게 도대체 무슨 소리인지 인지불가이거나 영어를 읽어주므로 영어발음을 잘 알아 듣지 못하면 이용할 수 없게 됩니다.

더욱이 외국인들도 G와 Z의 발음은 대부분 "쥐~"와 같이 발음되어 혼동할 수 있어 Captcha의 문자를 읽어줄때면 군대에서 읽듯 A는 "에이"라고 발음하지 않고 "알파"라고 발음합니다. ABC라는 문자열이라면 "에이비씨"가 아니라 "알파브라보찰리"와 같이 읽어 준다는 것이죠. 따라서 이런 내용을 모르는 사람이라면 이용이 불가능하겠죠?

즉, reCaptcha를 국내 웹사이트에서도 많이들 가져다 쓰지만 오히려 접근성은 왕창 떨어지는 결과를 초래합니다. 국내의 오픈소스 게시판중 유명한 "그누보드"가 웹 접근성이 적용된 버전을 내놓았다고 했을 때 과연 Captcha는 어떻게 구현했을까? 궁금했습니다. 그전에도 Captcha는 지원하고 있었고 단순하지만 숫자 5~6개를 입력하는 Captcha를 지원하고 있던터라 더더욱 궁금했습니다.

결론적으로 말하자면 그누보드에서는 Captcha에서 제공하는 숫자를 이미지 Captcha로 여전히 지원하면서 다만 각각의 숫자에 해당되는 mp3파일을 동적으로 로드해서 하나의 mp3로 합친 후 웹사이트에서 재생해 주는 구조로 되어 있습니다. 간단하지만 아주 웹 접근성을 잘 이해하고 준수하고 있다고 생각합니다. 

 

참고 : 음성 캡챠와 관련하여서는 https://captcha.com/audio-captcha-examples.html 를 참조하시도 도움 되실 겁니다. 

 

그렇다면 ColdFusion에서는 어떻게 Captcha의 내용에 해당되는 mp3파일(또는 다른 포맷의 음성파일)을 동적으로 합성하여 서비스 할 수 있을까요? 최근 제가 다니는 회사에서 ColdFusion으로 개발중인 한 지역 방송국의 홈페이지에서 웹 접근성을 준수한 Captcha의 구현이 반드시 필요 했습니다. 일단 컨셉은 그누보드와 유사하게 만들자였는데 문제는 mp3와 같은 Binary 파일을 합치는 방법에 대해 고민을 했죠. PHP의 경우에서는 비교적 간단하게 구현되었지만 ColdFusion은 Java로 작성되고 서비스되는 Application server이므로 Java를 응용하면 된다는 것 이였지만 생소한 방법이라 구현에 대한 팁을 얻기 힘들었습니다만 해외의 경우 ColdFusion 사용비율이 높기 때문에 몇번의 구글링으로 "이미지파일(Binary)을 자르고 합치는" 예제를 얻을 수 있었습니다. 이를 응용하여 아래와 같은 웹 접근성을 준수하는 Captcha를 만들 수 있었습니다. 

 

아래의 코드는 새로 고침을 할 때마다 임의의 숫자가 나열 됩니다. 숫자는 0부터 9까지의 숫자를 5개를 골라서 랜덤하게 출력됩니다. 물론 각각의 숫자에 해당되는 mp3파일을 바이너리로 읽어와 동적으로 합성하여 하나의 파일로 만들어 임시파일을 만들게 됩니다. Captcha코드나 이 임시파일명을 Session을 만들어 전송한 후 유효성이 인정되면 임시파일은 삭제하는 코드를 만들어 사용해야 겠죠? 물론 접속자마다 Unique하게 생성되므로 사용자가 브라우저를 그냥 닫아버리거나 한 경우 남아있는 임시파일을 지우는 코드를 별도로 만들고 ColdFusion의 스케줄러를 이용해서 주기적으로 삭제하는 것도 서버공간을 낭비하지 않는 방법일 수 있습니다.(그누보드에서는 그냥 다 남겨 두더군요. ㅠㅠ)

몇십줄 안되는 코드지만 대부분 Captcha에서 필요한것은 구현되어 있습니다. 각각의 소스코드내에 주석을 달아 두었으므로 살펴보시길 바랍니다. 가장 핵심은 Captcha의 랜덤생성 부분(4~18번째 줄까지)과 Mp3의 로드 및 합성 부분(19~53번째줄)입니다.

<cfprocessingdirective pageencoding="utf-8" />
<cfcontent type="text/html; charset=utf-8">

<!--- CAPTCHA용 문자열. --->
<cfset strList = "0,1,2,3,4,5,6,7,8,9" />

<!--- captchaLetter 구조체 생성 --->
<cfset captchaLetter = StructNew() />

<!--- 5개만 랜덤하게 추출 --->
<cfloop condition="(StructCount(captchaLetter) LT 5)">
	<cfset intIndex = RandRange(1, ListLen(strList)) />
	<cfset captchaLetter[ListGetAt(strList, intIndex)] = true />
</cfloop>

<!--- 추출된 5개의 숫자 --->
<cfset captchaStr = #structKeyArray(captchaLetter)# />

<!--- mp3파일 바이너리 길이를 담을 임시 변수 ---->
<cfset audioArray = 0>

<!--- CAPTCHA 문자의 숫자 만큼 루푸 --->
<cfloop from = "1" to = "#arrayLen(captchaStr)#" index = "i">

	<!--- captchaStr 문자열에 해당하는 mp3 파일 로드 --->
	<cffile action = "readBinary" file = "#expandPath('./sound/#captchaStr[i]#.mp3')#" variable = "audio" />
	
	<!--- 각 mp3의 배열길이를 하나로 합침 ---->
	<cfset audioArray = audioArray + ArrayLen(audio) />
	
</cfloop>

<!--- java.nio.ByteBuffer 오브젝트 생성 --->
<cfset objByteBuffer = CreateObject("java",	"java.nio.ByteBuffer") />

<!--- 각각의 mp3의 배열 길이의 합계만큼 먼저 메모리상에 Allocate 함. 즉, 모든 mp3의 배열길이의 합 --->
<cfset audiofull = objByteBuffer.Allocate(JavaCast("int", (audioArray))) />

<!--- 각각의 mp3를 가져와 합치는 루프 ---->
<cfloop from = "1" to = "#arrayLen(captchaStr)#" index = "i">

	<cffile action = "readBinary" file = "#expandPath('./sound/#captchaStr[i]#.mp3')#" variable = "audio" />
	
	<!--- audiofull 변수에 각각의 mp3 배열길이만큼 Put 함 --->
	<cfset audiofull.Put(audio, JavaCast("int", 0), JavaCast("int", ArrayLen(audio))) />
	
</cfloop>

<!---파일명 임의의 문자열 생성 --->
<cfset tempMP3 = "#createUUID()#" >

<!--- audiofull 값을 mp3파일로 저장 --->
<cffile action="write" file="#expandPath('./temp/#tempMP3#.mp3')#" output="#audiofull.Array()#" mode="777" />


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CAPTCHA DEMO</title>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script>
$(function() {
	//그누보드 CAPTCHA 미리듣기 jQuery 인용
    $("#captcha_mp3").click(function(){	
        var mp3_url = this.href;
        var html5use = false;
        var html5audio = document.createElement("audio");
        if (html5audio.canPlayType && html5audio.canPlayType("audio/mpeg")) {
            var wav = new Audio(mp3_url);
            wav.id = "mp3_audio";
            wav.autoplay = true;
            wav.controls = false;
            wav.autobuffer = false;
            wav.loop = false;            
            if ($("#mp3_audio").length) $("#mp3_audio").remove();
            $("#captcha_mp3").after(wav);
            html5use = true;
        }         
        if (!html5use) {
            var object = '<embed id="mp3_embed" src="' + mp3_url + '" autoplay="true" hidden="true" volume="100" type="audio/x-wav" style="display:inline;" />';
            if ($("#mp3_object").length) $("#mp3_object").remove();
            $("#captcha_mp3").after(object);
        }        
        return false;
    });
});
</script>
</head>

<body>
<!--- CAPTCHA 이미지 생성 ---->
<cfimage action="captcha" difficulty="medium" fontSize="25" width="220" height="50" text="#REReplace(ArrayToList(captchaStr, ","), ",", "", "ALL")#" fonts="Arial,Courier New,Courier" />
<a href="./temp/<cfoutput>#tempMP3#</cfoutput>.mp3" id="captcha_mp3" target="_blank" title="인증코드 듣기"><img src="./images/sound.png" border="0" alt="인증코드 듣기"></a>
</body>
</html>

ColdFusion의 은 파일을 핸들링할 수 있는 태그인데, 특정 파일을 바이너리형태로 읽어 들여 배열로 저장할 수 있습니다. 물론 여러 파일을 동시에 읽어들여 배열화 할 수 있는데 각각의 파일의 배열의 길이를 구하여 그 길이만큼의 빈 공간에 하나씩 더해가서 저장하는 반복으로 여러 바이너리파일을 순차적으로 합성합니다. (이 부분은 Java를 이용) Mp3 뿐만 아니라 이미지, 일반 문서파일의  분할과 결합을 할 수 있는 것이죠. 어렵지 않은 개념이지만 ColdFusion으로 구현하다보니 조금 생소했습니다. 이것 때문에 Facebook의 ColdFusion Developer 그룹에 물어봤는데 도움은 못되었지만 제가 참고한 소스를 그곳에도 남겨두었습니다. 웹 접근성을 준수해야 하는 최근의 웹사이트 구축환경에서 다양한 언어로 비슷하게 구현해 볼 수 있을 것 입니다. 

 

 

Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

CFFTP태그는 손쉽게 ColdFusion 문서에서 외부의 FTP서버에 연결해서 해당 내용을 가져오거나 파일을 업로드하거나, 다운로드할 수 있는 아주 유용한 태그입니다. 마치 ColdFusion웹문서가 FTP클라이언트처럼 작동하게 해주는 강력한 기능을 제공합니다. 사용방법도 너무 쉬워서 몇줄의 코드로 간단하게 구현할 수 있습니다.

그런데 ASP와 같은 Windows계열의 웹개발언어에서는 CP949와 같은 운영체제의 인코딩과 EUC-KR과 같은 웹페이지 인코딩으로 기본적으로 한글을 사용하기에 매우 편리합니다만, 웹개발시 한글과 영문외의 문자 가령, 일본어 등과 같은 경우엔 별도의 조치를 취해주어야 합니다.

반면 대부분의 Linux환경에서는 UTF-8을 기본 지원하고 있어서 특별히 문자열에 대한 고민을 할 필요는 없습니다만, JVM에 기반한 웹언어를 사용시에는 유니코드를 사용할때 추가적인 조치를 해줘야 합니다.

사실 Windows나, Linux나 ASP나 PHP나 또는 JSP, ColdFusion과 같은 대부분의 것들이 영어권국가(미국)에서 만들어지고 개발되어진 탓에 그들의 언어에 태생적으로 얽여있다는 이유 등으로 그들은 특별한 조치없이 기본적으로 지원되는 ISO-8859-1과 같은 인코딩을 이용하여 편리하게 이용하는 반면, 한글과 같은 2Byte 문자권에서는 늘 무언가의 조치를 해줘야 하는 불편함이 있기는 매 한가지 입니다.

최근에 Ubuntu Linux에 구축된 외부의 VSFTP서버에서 디렉토리와 파일목록을 가져와야 하는 작업을해야 할 상황이 생겼습니다. 기본적으로 Ubuntu는 UTF-8환경이고, FTP의 디렉토리 및 파일명 역시 UTF-8 형식으로 만들어지고 저장된 파일이여서 큰 걱정을 안했는데 역시 불길한 예감은 틀린적이 없었습니다.

한글로 된 모든 디렉토리명 및 파일명이 깨져서 반환되는 것이었습니다. 실행되는 ColdFusion문서를 UTF-8로 처리하도록 해보았지만 역시나 동일한 결과. 그리하여 생각해낸게 Java로 아예 파일 및 디렉토리명의 문자열이 무슨 인코딩인지 알아보기로 했습니다. 

간단하게, 구글링을 통하여 Detecting Character Encoding In Coldfusion 글을 참고하여 테스트한 결과 Latin1 즉, ISO-8859-1인코딩으로 디렉토리명과 파일명이 반환됨을 알게되었죠. 아래 그림을 참고하시면 이해가 빠르실 겁니다.

 

한글 디렉토리명과 파일명이 깨져보이는 현상

 

그림에서와 같이 어떤 FTP서버내에는 vod라는 디렉토리가 있는데 그 하위에 "스페셜"이란 한글로 명명된 디렉토리가 있습니다. 그리고 그 안에는 2개의 mp4파일이 존재하는데 각각 한자와 한글로 된 파일명을 가지고 있습니다.

일반적으로 CFFTP태그로 덤프(위 그림의 보라색 표 부분)를 해보거나 단순히 path와 같은 속성값을 출력해 보면 위 그림의 윗 부분처럼 한글(한자)로 된 부분이 깨져버립니다. 실행되는 ColdFusion문서자체가 UTF-8로 저장되고, 실행되고, 원격지의 FTP서버가 UTF-8로 운영되어도 말이죠. 아마도 추측컨데 JVM의 문제일듯합니다. 문자셋 문제는 대부분.

자, 그럼 위 그림의 UTF-8부분처럼 정상적으로 디렉토리명과 파일명을 가져오려면 어떻게 해야 할까요? 바로 파일을 가져올 디렉토리명의 한글을 ISO-8859-1로 그리고 반환되는 파일명을 UTF-8로 동적으로 변환해서 처리하면 됩니다. 

 

<cfprocessingdirective pageencoding="utf-8">
<cfcontent type="text/html; charset=utf-8">

<!--- ISO-8859-1(Latin1)을 UTF-8로 --->
<cffunction name="convertToUTF8" access="private" returntype="string" output="false">
    <cfargument name="txtString" type="string" required="true" />
	<cfset encoder = createObject("java", "java.nio.charset.Charset").forName("ISO-8859-1").newEncoder() />
	<cfif encoder.canEncode(txtString)>
        <cfset convertedTxt = createobject("java", "java.lang.String").init(txtString.getbytes('ISO-8859-1'),'UTF-8').tostring()>
	</cfif>	
	<cfreturn #convertedTxt# />
</cffunction>

<!--- UTF-8을 ISO-8859-1(Latin1)로 --->
<cffunction name="convertToLatin1" access="private" returntype="string" output="false">
    <cfargument name="txtString" type="string" required="true" />
	<cfset encoder = createObject("java", "java.nio.charset.Charset").forName("UTF-8").newEncoder() />
	<cfif encoder.canEncode(txtString)>
        <cfset convertedTxt = createobject("java", "java.lang.String").init(txtString.getbytes('UTF-8'),'ISO-8859-1').tostring()>
	</cfif>	
	<cfreturn #convertedTxt# />
</cffunction>

<!---FTP연결 시작--->
<cfftp action="open" connection="ftpQuery" username="demouser" password="demopasswd" server="someserver" stopOnError="yes"> 

<!--- 
FTP디렉토리에 한글이 있는 경우 해당 디렉토리를 불러오려면 convertToLatin1 함수를
해당 디렉토리 및 파일명이 한글인 경우 깨지는 것을 막기위해 convertToUTF8 함수를
--->

<!--- 디렉토리 및 파일리스팅 --->
<cfftp action="listdir" stopOnError="yes" name="getDirFiles" directory="#convertToLatin1('/vod/스페셜/')#" connection="ftpQuery">

<cfoutput query="getDirFiles">
	#convertToUTF8(path)# <br />
</cfoutput>

<hr />
<cfdump var="#getDirFiles#">

<!--- FTP연결 삭제 --->
<cfftp action="close" connection="ftpQuery" stopOnError="Yes"> 

위의 코드를 보죠. 위의 코드는 어떤 FTP에 연결과 "/vod/스페셜"이라는 디렉토리에서 파일들의 목록을 가져오는 코드입니다. 먼저 "/vod/스페셜"이라는 한글이 포함된 디렉토리명은 ISO-8859-1로 변환해서 ColdFusion서버에게 전달하고, ColdFusion서버에서 해당 FTP의 파일목록을 반환할때는 UTF-8로 반환되도록 함수를 2개 만들었습니다. 각각, convertToUTF8과 convertToLatin1라는 사용자정의함수(UDF : User Defined Function)입니다.

중간의 코드중에 directory="#convertToLatin1('/vod/스페셜/')#" 부분과 #convertToUTF8(path)# 부분에 각각 해당 함수가 쓰여 자동으로 문자열의 인코딩을 변경합니다. 

대부분 CdolFusion에서 로컬서버 및 원격서버와의 파일송수신, 파일목록의 반환, 압축(CFZIP) 등의 작업을 처리할때 한글파일명과 디렉토리명이 늘 깨져 말썽인데요. 이 함수를 적당히 응용하면 대부분의 문제는 해결되리라 봅니다. 

중요한 것은 ColdFusion서버가 Windows에서 구동되는 경우에는 이러한 문제가 거의 없다는 겁니다.(Windows가 좋다기보다 워낙 Windows는 모든 시스템의 문자셋을 해당 현지언어로 셋팅해버리기 때문에) 이 경우는 대부분 Linux에서 설치된 ColdFusion서버에서 발생하는 상황입니다. 혹시나  Linux나 Mac, Solaris와 같은 운영체제에서 ColdFusion을 구동하는 경우 한글처리에 도움되시길 바라는 마음에서 몇자 남깁니다. (물론, 중국어나 일본어도 동일하게 사용할 수 있겠죠?) 

Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

Adobe ColdFusion은 Trial 버전으로 사용하고 Trial 기간이 끝나면 10개의 Connection으로 제한되는 모든 기능을 사용할 수 있는 Deveolper edtion으로 작동합니다만, Trial을 다운로드하려면 Adobe.com에 반드시 회원가입을 해야 하는 불편함?이 있습니다. 

이때 이용할 수 있는 것이 Express edition인데요, 다음의 링크에서 운영체제별로 다운로드 할 수 있습니다.

https://www.adobe.com/products/coldfusion-family.html

 

Adobe ColdFusion family

ColdFusion Enterprise A platform ideally suited to create scable, high-performing web and mobile applications. Loading... Buy now Try now ›

www.adobe.com

 

위 링크 중간 부분에 Express edition이 있습니다. 다운로드하여 테스트환경에서 개발도구로 활용할 수 있습니다.

Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요

ColdFusion 웹어플리케이션서버들은 JSP등과 마찬가지로 대부분 Java기반 WAS의 JVM메모리의 관리를 받습니다. 문제는 가비지콜렉션을 따로 할 필요까지는 없지만 한정된 자원(메모리)를 사용하는 특성상 메모리 회수가 빠르게 안될 경우 다중사용자가 접속하여 사용하는 웹사이트 등 메모리활용도가 높은 경우엔 서비스에 문제가 발생할 수 있죠. 그렇다면 어떻게 ColdFusion페이지에서 가비지콜렉션을 실행할 수 있을까요? 결론은 JSP 등과 동일하게 실행하면 됩니다.  

<!--- Java Obj 생성--->
<cfset javaSys = createobject("java", "java.lang.System")>
<!--- 가비지콜렉션실행 --->
<cfset javaSys.gc()>
<cfset javaSys.runFinalization()>


하지만 일반적으로 가비지콜렉션이나 파이널라이저를 쓰지말도록 권장하더군요. 실행중인 어플리케이션에 문제가 발생할 수 있으니까요. 다만 메모리의 사용량 등을 모니터링해서 적절히 어플리케이션에 필요한 메모리확보 및 시스템 점검을 위한 사전예방은 아무리 강조해도 지나치지 않습니다. 간단하게 ColdFusion에서 사용중인 메모리를 살펴보려면 다음과 같이 Pete Freitag의 포스트(http://www.petefreitag.com/item/115.cfm)와 같이 간단한 소스로 볼 수 있습니다.

Posted by nooree nooree 트랙백 0 : 댓글 0

댓글을 달아 주세요