요즘 글쓰기를 모두 Obsidian 으로 하고 있다. 물론 Evernote나, Notion 과 같은 도구들이 대세이긴 하다. 특히 자체적으로 클라우드로 데이터를 송/수신하고 있어 어느 장비에서 작성해도 어느 장비에서도 동일한 내용을 확인할 수 있는 많은 장점이 있다.
그에 반해 Obsidian의 경우 유료 가입을 하지 않는 경우 데이터 공유같은 훌륭한 기능은 제공하지 않는다. 굳이 공유하고 싶다면, Onedirve나, Google Dirve와 같이 파일 공유 공간에 저장하고, 그 공간을 공유하는 방식이 유일하다.
Markdown!
하지만, 글 작성할 때, Markdown 이라는 작성방식을 사용하는데, 이게 은근 매력이다. 정말 다양한 Format을 제공하지는 않지만, 내가 의도한 글을 작성하는데 하등 부족함이 없다. 그리고 특정 기능을 꺼내기 위해 마우스로 버튼을 이곳 저곳 누를 필요도 없이, 직접 Text로 입력하면 기능이 활성화 되서 적용이 된다 게다가 Markdown으로 작성하면 자동적으로 실제 보여지는 화면으로 바로 바로 전환되고, 필요하면 바로 바로 Markdown 양식을 편집할 수 있다.
여튼... 이런 많은 장점이 있어, 메일을 보내기 전에 내 생각을 이곳에 적는데, 문제는 여기에 적힌 글을 그대로 복사한 뒤, Outlook 텍스트에 붙이면 Markdown 양식으로 그대로 붙는다.
문제! Outlook 으로 어떻게 보내나?
Obsidian에서는 Html 형식으로 깔끔하게 작성된 내용이 Markdown 형식으로 그대로 붙이니.. 다시 편집해야 되나 싶었다.
HTML로
그래서 Outlook 내에 Markdown 지원여부 방법이나, 이 Markdown 내용을 변환할 무언가를 계속 찾았다.
그러다가, Obisidian의 커뮤니티 플러그인 중 "Copy doucment as HTML" 이라는 것을 찾았다.
이것을 깔아서 사용해보니, 문서 옵션에 Copy as HTML 명령이 생겼다.
이것을 통해 Markdown으로 만든 문서를 HTML로 끄집어 낼 수 있었다.
이 다음에 Outlook 으로 이 HTML을 밀어 넣는 방법인데... 없었다.!!!!!
그러다가, 바탕화면에 export.html 이라는 이름의 파일을 만든 뒤, 메모장을 이용해 앞서 Copy as HTML 한 내용을 그대로 붙여서 저장했다. 그리고 웹브라우저로 띄우니 왠걸 그럴싸하게 나온다.
이 내용을 그대로 긁어 Outlook 편집기에 붙이니 생각보다 잘 붙었다.
폰트 좀 조정하는 정도로 대략 모양도 그대로 옮겨진것 같았다.
(물론 이미지라든가, 특수 표현은 좀 이상하게 붙긴 했다.)
나중에 메모한 내용을 메일로 보낼때는 좀 편해질 것 같다.
정리
1. Obsidian 으로 작성한다.
2. Copy as HTML 로 HTML 코드를 클립보드에 담는다.
3. export.html 파일을 텍스트 편집기로 열고 클립보드의 HTML을 그대로 붙여 넣는다.
4. 웹브라우저를 이용해 export.html 을 띄운다.
5. 웹브라우저 상에 있는 화면을 모두 복사해서 Outllok 편집기에 붙여 넣는다.
단, 이것은 Outlook과 같이 HTML 편집기를 제공하지 않는 곳에서 발생되는 문제고, 대부분 HTML 소스를 직접 수정하기 위한 옵션을 제공한다면, HTML코드를 그대로 붙이는게 더 간단하고 빠르다!
위의 내용 처럼 Argument 값을 정규식에 대입하려면 Argument 전체 값이 필요한데, Main(string [] args)를 통해서 받은 args 값은 공백으로 전부 짤라놔서, 저 정규식을 대입해봐야 아무 도움이 안된다. 즉 자르기 전, 원본 Arguments 값을 얻어와야 한다.
일단 여기 접속하면 다양한 ActiveX나 관련 모듈들을 설치한다. 애초에 Internet Explorer를 쓰라고 나오긴 하는데, Chrome이나 Firefox에도 되긴한다. veraport-g3-x64 를 설치하면 대부분 관련된 대부분은 설치되긴 한다.
그런데, 이상하게 발급, 출력 부분에서 발급하려고 "결제", 상단에 나오는 팝업에서 "열기"를 하는 순간, RPRTRegisterXCtrl.exe 를 실행한다고 한다. 그래서 실행해! 라고 했는데.... 반응이 없다. 계속 기다려봤는데.. 역시 아무것도 동작하지 않는다.
처음에는 내 쪽에서 설정되거나 다른 프로그램과 오류가 있는줄 알았는데... 아니였다.
문제 원인
처음에는 단순 프로그램 충돌, 가상화시스템, 네트워크 ... 뭐 등등 다양한 부분에서 접근했다. 심지어 회사에서 놀고 있는 노트북이 있어서 거기다가도 했는데 동일했다. 단순 열람이나, 전자정부문서 지갑 전송 같은데서는 아무 문제가 없는데, 유독 이 "출력", "발급"에서는 터진다.
한 2시간 정도 삽질하다가.. Event Viewer를 보는데... 메시지가...
"C:\Program Files (x86)\markany\maepscourt\rprtregisterxctrl.xgd"에 대한 활성화 컨텍스트를 생성하지 못했습니다.
종속 어셈블리 Microsoft.VC90.MFC,processorArchitecture="x86",publicKeyToken="1fc8b3b9a1e18e3b",type="win32",version="9.0.21022.8"을(를) 찾을 수 없습니다.
자세한 진단을 위해서는 sxstrace.exe를 사용하십시오.
....
뒷골이 싸했다.
패키징 실패. 이 프린터용 프로그램인 RPRTRegisterXCtrl.exe 패키징할 때, 이에 연관된 라이브러리를 같이 설치하지 않은 것이다. 만일 사용자가 오랫동안 포멧하지 않고 사용하면서 다양한 프로그램을 깔았다면, 분명 설치되어 있을 라이브러리 겠지만, 나 같이 깔끔하게 포멧을 하고 새로 설치해서 사용하는 사람의 경우 저 라이브러리가 없을 가능성이 높은데... 이거 패키징 한 친구는 지 컴퓨터에서 잘 된다고 넘어간듯...
기존에 Yona 를 구축해서 버전관리를 잘해오고 있는데, 문제는 알림 메일이였다. 지금까지 계속 google의 SMTP로 잘 운영해오다가, 어느 순간 google의 로그인 방식이 보안 정책에 따라, OAuth 방식이 아니면 더 이상 예전 처럼 아이디/패스워드 기반의 로그인을 지원하지 않는 것이다. 그래서 naver.com 내에 계정을 하나 파서 그 안에 있는 POP3/IMAP 기능을 활성화 했고, 거기서 제공하는 SMTP 설정 값을 이용해 Yona의 application.conf 값을 아래와 같이 수정했다.
2024-03-06 13:26:51,466 - [WARN] - from application in play-akka.actor.default-dispatcher-974
Failed to send a notification: org.apache.commons.mail.HtmlEmail@1720365d
org.apache.commons.mail.EmailException: Sending the email to the following server failed : smtp.naver.com:465
at org.apache.commons.mail.Email.sendMimeMessage(Email.java:1410)
at org.apache.commons.mail.Email.send(Email.java:1437)
at info.schleichardt.play2.mailplugin.MailPlugin$$anonfun$6.apply(MailPlugin.scala:60)
at info.schleichardt.play2.mailplugin.MailPlugin$$anonfun$6.apply(MailPlugin.scala:54)
at info.schleichardt.play2.mailplugin.MailPlugin.send(MailPlugin.scala:68)
at info.schleichardt.play2.mailplugin.api.Mailer$.send(Mailer.scala:8)
at info.schleichardt.play2.mailplugin.Mailer.send(Mailer.java:15)
at controllers.ProjectApp.sendTransferRequestMail(ProjectApp.java:775)
at controllers.ProjectApp.transferProject(ProjectApp.java:634)
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$191$$anonfun$apply$191.apply(routes_routing.scala:3708)
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$191$$anonfun$apply$191.apply(routes_routing.scala:3708)
at play.core.Router$HandlerInvokerFactory$$anon$4.resultCall(Router.scala:264)
at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.invocation(Router.scala:255)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:55)
at Global$2.call(Global.java:274)
at actions.AnonymousCheckAction.call(AnonymousCheckAction.java:57)
at actions.IsAllowedAction.call(IsAllowedAction.java:68)
at actions.AbstractProjectCheckAction.call(AbstractProjectCheckAction.java:93)
at play.db.ebean.TransactionalAction$1.call(TransactionalAction.java:21)
at play.db.ebean.TransactionalAction$1.call(TransactionalAction.java:18)
at com.avaje.ebeaninternal.server.core.DefaultServer.execute(DefaultServer.java:715)
at com.avaje.ebeaninternal.server.core.DefaultServer.execute(DefaultServer.java:709)
at com.avaje.ebean.Ebean.execute(Ebean.java:1264)
at play.db.ebean.TransactionalAction.call(TransactionalAction.java:18)
at play.core.j.JavaAction$$anonfun$11.apply(JavaAction.scala:82)
at play.core.j.JavaAction$$anonfun$11.apply(JavaAction.scala:82)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:40)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:46)
at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:32)
at scala.concurrent.impl.Future$.apply(Future.scala:31)
at scala.concurrent.Future$.apply(Future.scala:485)
at play.core.j.JavaAction$class.apply(JavaAction.scala:82)
at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.apply(Router.scala:252)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:129)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:128)
at scala.Option.map(Option.scala:145)
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:128)
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:121)
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:483)
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:483)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:519)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:519)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:496)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:496)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:41)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: javax.mail.MessagingException: Could not connect to SMTP host: smtp.naver.com, port: 465;
nested exception is:
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1972)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:642)
at javax.mail.Service.connect(Service.java:317)
at javax.mail.Service.connect(Service.java:176)
at javax.mail.Service.connect(Service.java:125)
at javax.mail.Transport.send0(Transport.java:194)
at javax.mail.Transport.send(Transport.java:124)
at org.apache.commons.mail.Email.sendMimeMessage(Email.java:1400)
... 56 more
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:171)
at sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:103)
at sun.security.ssl.TransportContext.kickstart(TransportContext.java:227)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:433)
at com.sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.java:548)
at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:352)
at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:207)
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1938)
... 63 more
그런데 에러를 찬찬히 살펴보니 아래와 같은 문장이 눈에 딱 띄었다.
Caused by: javax.mail.MessagingException: Could not connect to SMTP host: smtp.naver.com, port: 465;
nested exception is:
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1972)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:642)
이상해서, 과거 Yona의 Issue 창을 뒤져보니, 답글 중에 아래와 같은 답변을 발견했다.
다른 곳에서 전체 백업된 SQL을 받아 데이터베이스를 생성했는데, 생성했더니, 데이터베이스의 Data Collate가 "Latin1_General_CI_AS"로 설정되어 있었다. 그래서 간신히 데이터베이스의 설정 값을 바꾸어 "Korean_Wansung_CI_AS"으로 바꾸긴 했다. 그런데 Query를 실행하니까, 웬걸...컬럼들은 여전히 "Latin1_General_CI_AS" 로 되어 있었고, 새로 만든 테이블 내의 값들과 비교하려는데 자꾸 다음과 같은 에러메시지를 뱉어됐다.
Cannot resolve the collation conflict between "Korean_Wansung_CI_AS" and "Latin1_General_CI_AS" in the equal to operation.
데이블 디자이너, 그러니까, 테이블 수정에 들어가 varchar 컬럼마다 데이터 정렬(Data Collate) 값을 바꾸면 되긴 하는데, 이 많은 테이블의 또, 그 컬럼들에 들어가 수정하려니 깝깝했다.
아래의 코드 값에서 @collate값만 Korean_Wansung_CI_AS로 변경하고, 수정할 데이터베이스의 쿼리창을 열고 실행했다.
DECLARE @collate nvarchar(100);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length int;
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);
SET @collate = 'Korean_Wansung_CI_AS';
DECLARE local_table_cursor CURSOR FOR
SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1
OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE local_change_cursor CURSOR FOR
SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
, c.name column_name
, t.Name data_type
, c.max_length
, c.column_id
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id
LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE c.object_id = OBJECT_ID(@table)
ORDER BY c.column_id
OPEN local_change_cursor
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id
WHILE @@FETCH_STATUS = 0
BEGIN
IF (@max_length = -1) OR (@max_length > 4000) SET @max_length = 4000;
IF (@data_type LIKE '%char%')
BEGIN TRY
SET @sql = 'ALTER TABLE ' + @table + ' ALTER COLUMN ' + @column_name + ' ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate
PRINT @sql
EXEC sp_executesql @sql
END TRY
BEGIN CATCH
PRINT 'ERROR: Some index or constraint rely on the column' + @column_name + '. No conversion possible.'
PRINT @sql
END CATCH
FETCH NEXT FROM local_change_cursor
INTO @row_id, @column_name, @data_type, @max_length, @column_id
END
CLOSE local_change_cursor
DEALLOCATE local_change_cursor
FETCH NEXT FROM local_table_cursor
INTO @table
END
CLOSE local_table_cursor
DEALLOCATE local_table_cursor
GO