반응형

이 포스트에서는 airbyte를 docker위에 구동시킨 이후에 대해서 다루기 때문에 설치에 대한 부분은 다른 포스트를 찾아보기를 바랍니다. 또는 airbyte 공식 홈페이지 https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart 부분을 참고하시기 바랍니다.

설치에 대한 부분은 추후에 업로드할 수도 있으나 지금은, sql server의 연결시 났던 에러를 기록하기 위함이기 때문입니다.

 

기본값으로 지정되어있는 경우

localhost:8000 접속

 

좌측 메뉴의 connections에서 new connection을 눌러도 되고,

sources와 Destinations를 따로 구축해서 그 후에 connections를 눌러도 됩니다.

저는 Sources를 눌렀습니다. 

 

[Sources에 등록 전 선행작업]

 

db를 enable CDC 시키기

 

참고로 구동한 airbyte( 기본값으로 지정되어있는 경우: localhost:8000)의 sources를 누르면 우측에 적혀있는 내용을 번역 또는 제가 이해한 방식으로 설명한 것입니다. airbyte공식홈페이지에도 같은 페이지를 확인할 수 있습니다.

 

 

1. sql server에 연결할 db에서 new query를 눌러

 

USE {database name}
GO
EXEC sys.sp_cdc_enable_db
GO

를 하고, 대상 테이블에는

 

USE {database name}
GO

EXEC sys.sp_cdc_enable_table
@source_schema = N'{schema name}',
@source_name   = N'{table name}',
@role_name     = N'{role name}',  [1]
@filegroup_name = N'{filegroup name}', [2]
@supports_net_changes = 0 [3]
GO

 

를 합니다.

 

[1] rolename에 값 대신 NULL을 사용하면, sysadmin과 db_owner 권한이 있는 계정만 접근이 가능합니다. 별도로 role을 만들어서 하는 방법이 있으나 이 포스트는 초기 연결 테스트를 위함으로 NULL을 입력합니다.

 

[2] 파일그룹을 구체화하려면 입력하지만, 입력하지 않으면 파일그룹의 기본값이 지정되므로 이 테스트에서는 [2]줄을 삭제했습니다.

 

최종적으로

 

EXEC sys.sp_cdc_enable_table
@source_schema = N'{schema name}',
@source_name   = N'{table name}',
@role_name     = NULL
@supports_net_changes = 0 
GO

에서 스키마와 테이블이름을 적으시면 되겠습니다.

 

제일 처음에 연결할떄는 스냅샷을 이용하기 때문에 다음과 같은 sql문도 사용하시면 되겠습니다.

 

ALTER DATABASE {database name}
  SET ALLOW_SNAPSHOT_ISOLATION ON;

 

새로운 계정에 role과 권한을 추가하여 사용하는 방법도 명시되어있으나

 

테스트 DB에 연결시도를 하기 위한 포스트이기때문에 sa계정을 사용했습니다.

 

 

 

db의 TCP 연결 포트를 방화벽을 통해 인바운드 규칙을 생성합니다.

이대로는 에러가 났어서 UDP 연결 포트로 1434를 열어주어야 한다고 하여 열어주었습니다.

 

---------------------------------------

이제 다시 airbyte 구동화면입니다.

 

아래와 같은 화면에서 Host, Port, Database, db계정 정보를 입력합니다.

 

 

Update Method는 Read Change using Change DData Capture(CDC)를 선택하고 Set up source를 누릅니다.

 

 

에러가 났습니다. 에러 파일을 열어보니 에러부분에 다음과 같은 에러가 났습니다.

 

Checking that the SQL Server Agent is running using the query: 'SELECT status_desc FROM sys.dm_server_services WHERE [servicename] LIKE 'SQL Server Agent%' OR [servicename] LIKE 'SQL Server 代理%' '


 Set initial fetch size: 10 rows


74 Exception while checking connection: java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0

 

 

 

에러를 추적해야했죠.

sql server new query를 열어

'SELECT status_desc FROM sys.dm_server_services WHERE [servicename] LIKE 'SQL Server Agent%' OR [servicename] LIKE 'SQL Server 代理%' '

쿼리를 똑같이 쳐봤습니다. 아무것도 안나옵니다.

 

WHERE 조건을 없애고 쳐봤습니다.

'SELECT status_desc FROM sys.dm_server_services

===>> SQL Server 에이전트 (인스턴스명)

이러한 형태의 값이 존재했습니다.

 

gpt에 이 값을 변경할 수 있는지 물어봤는데 변경할 수 없다고 하더라구요.

sql server 설치파일에서 저 이름을 본적이 있어서 설치과정을 진행해봤는데, 거기서는 명칭을 변경하여 db를 설치할 수 없었습니다.

 

Sql Server Configuration Manager에서도 해당 이름을 바꿀 수 없었죠.

한글이 가용한 윈도우에서 sql server를 설치하면 저와 같은 증상을 겪으실 것 같네요. 문제를 겪으신 독자분들은 다행히 해결책을 찾았으니 계속 읽으시면 되겠습니다.

 

 

그런데 Window의 시스템을 읽어오는 거라면 Registry에 있을꺼라는 확신이 들어 찾아보았습니다.

 

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services에서 값들을 찾아보면,

제 경우에는 기본 인스턴스로 SQLSERVERAGENT 내에 DisplayName에 존재하고 있음을 발견했습니다.

 

여기에서 가운데 한글로 명명된 '에이전트' 부분을 Agent로 변경 후 컴퓨터를 재시작했습니다.

(에이전트라는 한글부분만 Agent로 변경하셔야 다른 에러나 문제를 피하실 수 있습니다. -

'SQL Server Agent (MSSQLSERVER)' )

 

 

재시작 후 Sql Server Configuration Manager 내 SQL Server 서비스 내에서 명칭이 변경된 것을 확인하고, 

 

sql server new query를 열어 조회가 되지 않아 에러가 났던 쿼리

'SELECT status_desc FROM sys.dm_server_services WHERE [servicename] LIKE 'SQL Server Agent%' OR [servicename] LIKE 'SQL Server 代理%' '

 

를 실행하니 정상적으로 레코드가 조회되었습니다.

 

그 뒤 airbyte를 실행하여 다시 연결을 시도한 결과 정상적으로 연결되었습니다.

반응형

'Common > Infra' 카테고리의 다른 글

VMware Workstation Pro 전면 무료화  (0) 2025.01.03
반응형

사내에서 테스트 서버 구성할때 Vmware Workstation Player 17버전을 쓰고 있었는데

player 버전은 snapshot이나 export to cvf 등이 막혀있어서 사용에 매우 불편했어요

 

그런데 17보다 이전버전의 Vmware Worksation Pro 라이센스가 있다는 것을 최근에 알아서, 이미지를 복사했는데 최근버전의 것을 이전버전으로 옮기는게 안되더라구요.

 

그러다 ovf로 변환 후 넘기는 방법이 있다는 것을 알게되었답니다. vmware workstation의 이전버전 뿐만아니라 hyper-v로의 이전 등 다른 가상머신 툴로도 옮기기가 가능하다고 하더라구요.

 

 

방법입니다. 다만, 저는 window에서 실행하고 있는 점을 유의해주세요! linux서버는 다루지 않고있습니다.

 

vmware Workstation player 17를 설치시에는

C:\Program Files (x86)\VMware\VMware Player\OVFTool 경로에 ovftool.exe라는것이 있습니다.

program Files에 있을 수도 있어요, 버전이 다른 경우 비슷한 경로를 탐색해보시면 되겠습니다.

 

 

 

 

찾으셨다면 cmd를 열어줍니다.

Win + R 로 cmd를 입력해주세요.

 

 

cmd 를 여시고 아까 ovftool.exe가 있던 경로를 탐색기에서 복사해줍니다.

 

위 스크린샷 처럼 탐색기에서 경로부분을 클릭 후 ctrl + c를 눌러주면됩니다.

또는 직접 기입하셔도 상관은 없습니다. 편리하게 알려드리려는 거니까요!

 

 

복사한 경로를 cmd 창에서 'cd'입력 -> 띄어쓰기 -> ctrl + v로 붙여넣기 해줍니다.

(저의 경우 ) ==> cd C:\Program Files (x86)\VMware\VMware Player\OVFTool

 

그러면 해당 폴더로 이동이 되는데요,

 

 

여기서 ovftool "가상머신의 경로 (.wmx 파일의 경로)" "ovf파일을 만들경로\filename.ovf"를 입력해 줍니다.

 

만일 어떤 에러가 난다면

ovftool  --X:logFile=ovftool-log.txt --X:logLevel=verbose "가상머신의 경로 (.wmx 파일의 경로)" "ovf파일을 만들경로\filename.ovf" 를 입력해주면 어떤 원인이 있는지 확인해볼 수 있습니다.

 

저같은 경우는 아래와 같이

Opening VMX source : (가상머신).vmx 

Error: Failed to open disk : (가상머신).vmdk

Completed with errors

라는 에러가 계속 났어요. 그래서 디버깅 방법을 찾아봤던 것이지요.  

 

이미지 손상에 대한 말로 repair 프로그램이 있다는데, vmware workstation player 버전에서는 존재하지 않고, vmware workstation  pro에만 있는 프로그램이었죠.

 

그래서 repair 프로그램을 구글링 하다가,

어제 가상머신을 종료하지 않고 피시를 종료했던 것이 기억나서, 가상머신을 다시 킨 후 정상적으로 종료를 했더니 문제가 해결되었습니다.

 

이 문제가 실제로 repair 프로그램으로 해결이 될 수 있는 부분이긴 했으나, 불필요한 과정을 겪지 말고 가상머신을 켜서 다시한번 정상종료를 해보시면 어떨까요. player 사용자분들은 export to cvf도 할 수 없고, vmware-vdiskmanager -R "<path of the vmdk(virtual disk>" 명령줄을 칠 대상인 vmware-vdiskmanager.exe 프로그램이 없기 때문에, 저와 같은 증상이 있는 경우 꼭 한번 해보시면 좋을 것 같습니다.

 

 

변환 성공! 이제 Pro license가 있는 Vmware Workstation을 사용하러 가겠습니다.

 

해결에 도움이 되시면 좋겠네요.

반응형
반응형

프로그램을 개발할때 많이 쓰는 것 중에는 사용자에게 어떤 알림메시지를 보내는 것입니다.

사용자에게 메시지를 보내는 방법은 크게

SMS(문자) 발송, 이메일 발송, 우리나라 기준 한정으로 카카오 메시지 발송이 있을 것입니다.

 

이번 포스트에서는 그중에서도 이메일 발송에 대해 알아보도록 하겠습니다.

 

대개 email을 발송하기 위해서는 

이메일을 발송하는 이메일 서버(SMTP)와 이메일을 발송할 이메일 계정이 필요합니다.

 

C#에서는

.Net 라이브러리 중 System.Net.Mail 라이브러리 내

 

SmtpClient 클래스로 SMTP와 연결하고,

MailMessage 클래스로 이메일을 작성할 수 있습니다.

다만 이 포스트에서는 메일서버 구축이 아닌 보내기에 초점을 두었기 때문에 SMTP서버의 구축 관련한 내용은 다루지 않겠습니다.

 

참고로 무료로 풀려있는 SMTP 서버들이라 하더라도 일정 갯수가 넘어가게 발송하게되면 상업적용도로 간주되어 결제를 요구하기도 하니, 상업적용도로 사용하실 분들은 SMTP서버를 제공하는 각 기업에 절차에 맞게 결제 등을 하여 사용하시면 됩니다. c# 개발자 또는 엔지니어라면 아마도 Microsoft, 한국에서는 역시 naver, 그리고 kakao도 daum을 인수했기 때문에 smtp서버가 있을 것이라 판단됩니다. 

 

SqlClient도 그렇고 호출을 담당하는 클래스들은 ~Client로 명명하는 것이 규칙으로 보입니다.

먼저, SmtpClient를 선언합니다.

    using SmtpClient SmtpServer = new(smtpAddress)
    {
        Port = smtpPort,
        EnableSsl = true,
        UseDefaultCredentials = false,
        DeliveryMethod = SmtpDeliveryMethod.Network,
        Credentials = new NetworkCredential(mailSender, mailSenderPassword)
    };

smtpAddress 자리에는 SMTP Server의 주소를 입력하면되고

smtpPort에는 SMTP Server의 열려있는 포트를 이용하면 됩니다. 보통 암호화 보안연결을 사용하면 465나 587, 사용하지 않으면 25가 기본값으로 알려져있습니다.(https가 443, http가 80 인 것과 동일하게 생각하시면 되겠습니다.)

SMTP서버를 직접 구축하신 경우에는 네트워크에 지정한 포트번호를 이용하시면 되겠습니다.

 

SMTP에 연결할 계정은 NetworkCredential() 클래스에 (계정, 비밀번호) 형태의 생성자가 있으므로, SmtpClient 클래스의 인스턴스 필드 중 Credentials로 입력해주면 됩니다.

 

SmtpClient는 IDisposabled이 구현되어있어서 선언부 앞에 using을 붙여주면 사용하지 않을때 메모리를 해체할 수 있습니다.

 

Visual Studio 기준으로 ctrl버튼을 누른채로 작성한 SmtpClient를 클릭하면, 해당 클래스를 모두 보실 수 있는데요.

이외에도 여러 필드가 있지만 포스트의 예제에서는 저 정도만 사용하도록 하겠습니다.

 

SMTP를 연결했으면 이제는 이메일을 작성해야겠죠.

    using MailMessage email = new()
    {
        From = new MailAddress(mailSender),
        Subject = emailSubject,
        BodyEncoding = Encoding.UTF8,
        IsBodyHtml = true,
        Body = "본문 내용",
    };

 

만일 받는이가 1명이라면 MailMessge(string from, string to)라는 생성자가 존재하기 때문에 아래와 같이 선언하는 방법도 있습니다.

    using MailMessage email = new(mailSender, mailReceiver)
    {
        Subject = emailSubject,
        BodyEncoding = Encoding.UTF8,
        IsBodyHtml = true,
        Body = "본문 내용",
    };

 

 

왜 선언부에 To, Cc, Bcc를 안적으셨냐고 물으실 수 있는데요, 여러명한테 보낼 수 있다보니 세 변수는 컬렉션으로 구성되어있어요. (class MailAddressCollection : Collection ) 한명한테만 보낸다면 모르겠지만, 여러명한테도 보낼 상황이 있는 작업물이라면 선언하고 추가하는 방식이 좋습니다.

email.To.Add(받을사람1);
email.To.Add(받을사람2);
email.Cc.Add(참조1);
email.Bcc.Add(숨은참조1);

 

실제로 사용하실때는 받을 사람 자체가 컬렉션에 있어서 루프문을 통해서 Add하는 방식으로 가게 되곤 합니다.

 

BodyEncoding을 UTF.8 로 해줘야 영어가 아닌 문자가 깨지는 것을 방지할 수 있고,

IsBodyHtml = true 를 사용하게 되면, <div></div> <p></p> 등 html을 이메일 본문 내에 사용하면 랜더링 해줍니다.

false를 사용하면 글자 그대로 나오게 됩니다.

 

이메일을 다 작성했다면 메일 보내기를 해야겠죠?

 

SmtpServer.Send(email);

비동기 방식의 SendMailAsync(MailMessage email) 도 존재하고, cancellationToken을 사용하신다면, SendAsync(MailMessage email, CancellationToken  cancellationToken) 도 있습니다.

 

저같은 경우에는 email Body에 html을 넣어서 테이블로 어떤 데이터를 정리해서 보내주는 형식으로 개발을 많이 해서 Body에 엄청난 노가다를 하곤 했었는데요. 여러분의 상황에 맞게 잘 활용하시길 바라겠습니다.

반응형
반응형

 

 

"[C#] 초성을 포함한 StartWith 함수 만들기" (링크: https://pichen.tistory.com/56 ) 글에서 ㄱ부터 ㅎ까지를 모두 직접 적어서 switch 돌리는 것에 계산식이 있지 않겠냐고 하는 반응을 보았습니다.

 

[C#] 초성을 포함한 StartWith 함수 만들기

개발자 중 누군가 초성을 포함하여 검색하는 방법을 물었는데, 시간이 남아서 개발을 했다가 초안이 맘에 안들어서 퇴근 후 공부를 마친 시간에 보완작업을 해보았습니다. 현재 다니는 회사에

pichen.tistory.com

 

결론적으로 말하면 1편에서 쓰는 방법을 쓰는 것이 성능면에서 월등해보입니다만, char에 대한 이해도를 높이기 위해 계산을 하는 과정을 보여드리려고 합니다. 먼저 각 글자들의 유니코드를 외우고 다니고 있지 않기 때문에 지난 글의 소스인

아래 이미지 부분을 복사하여 엑셀을 열어 붙여넣기합니다. 엑셀을 활용하는 방법은 다른 글 또는 검색으로 찾아보세요!

(이 글을 작성하는 24년 5월 9일까지는 엑셀 관련글은 블로그에 없긴 합니다.)

 

 

 

엑셀에 붙여 넣기 후 [텍스트 나누기] 기능으로 텍스트 중 유니코드가 궁금한 '' 들을 분리해냅니다.

 

그리고 나서, 컬럼만 복사 후 값으로 붙여넣기한 다음에 로그를 찍을 모양을 만들어 줍니다.

아래 이미지 처럼 만들때 당연하게 손으로 쓰는 부분은 L 컬럼 뿐이어야 엑셀을 쓰는 보람이 있습니다.

M열 중 첫행 하나의 값을 만들고 + 눌러서 누르면 됩니다.

참고로 M1의 셀에 입력한 값은 =$L$1&I1&$L$2&$L$3&I1&$L$4&$L$1&J1&$L$2&$L$3&J1&$L$4&$L$1&K1&$L$2&$L$3&K1&$L$2

이건데요, 실제로는 셀 클릭 -> & -> 셀클릭 방식으로 만든거예요. 열과행에 $가 붙는 것은 F4를 누르면 만들 수 있고 자동완성시 고정될 셀에 지정하면 됩니다.(L컬럼의 값들)

로그를 찍어보려고 하는거라 결국은 복사하여 IDE에 붙여넣기 합니다!

Console.Write($@"          요기에 붙여넣기 하세요!       ");

 

$@를 둘다 붙이는 이유는 $는 보간문자열을 이용하기 위해서고 (그래서 엑셀에서 굳이 {와 }를 붙여서 가져왔어요.

@를 안쓰면 문장 띄어쓰기할때마다

$" ~~~~ " +

$" ~~~~ " +

$" ~~~~ " 이렇게 붙여줘야하는데요, @를 쓰면 이 과정을 생략해도 일관된 값으로 처리됩니다! @의 다른 모든 기능들은 검색해보세요!

 

출력값은 이렇게 나옵니다.

 

이제 초성 검색을 위해 필요한 유니코드들을 알았죠!

유니코드간의 규칙을 찾기 위해 다시 엑셀로 붙여넣기 하고 앞서 설명드렸던 텍스트 나누기 - 기준 "공백"을 적용하여

숫자만 분리해낸 뒤, 제가 ㄲㄸㅃㅆㅉ를 제일 뒤에 적었어서, 자음의 유니코드 순서에 맞게 정렬합니다. 엑셀의 필터기능을 이용하면 쉬운데, 방법을 모르신다면 수동으로 정렬하셔도 좋습니다.

 

 

유니코드가 비어있는 부분을 혹시 캐치하셨나요? ㄲ과 ㄴ 사이 12595가 없고, ㄹ과 ㅁ 사이에 12602~12608이 없죠,

위에 로그를 찍던 방식으로 찍어보시면 알겠지만 ㄳ, ㄺ,ㄻ,ㄼ,ㄽ,ㄾ,ㄿ,ㅀ 로 초성으로는 올 수 없는 애들이 껴있어요.

가-힣에는 해당 초성이 받침 이외에서 쓰이는 글자는 없죠!

그래서 이 부분들만 남기면 약간의 규칙이 생긴답니다.

 

 

ㄱ 과 가 간에는 31439 차이가 있는데, 가 -> 까, 까 -> 나 등등 은 유니코드 차이가 정확히 588이라는 얘기입니다.

또한 가와 깋, 그러니까 ㄱ으로 시작하는 가장 첫 글자와 ㄱ으로 시작하는 가장마지막 글자의 차이가 587,

어떤 초성을 고르더라도 그 차이는 동일하게 587이라는 결과가 위 엑셀 캡처에서 드러났습니다.

 

따라서, char 'ㄱ'의 int 값 12593에 31439를 더하면 '가'의 int값이 되고, 여기서 587을 더하면 '깋'의 int 값이 됩니다.

char'ㄴ'의 int 값은, 12593 ('ㄱ') + 31439 + 588, 여기서 582을 더하면 '닣'의 int값이 됩니다.

 

이해가 되시나요?

 

이 내용을 c#에서 적어보면, 아래와 같습니다. 규칙이 보이시나요?

char gi_eok = 'ㄱ';
char ga = (char)((int)gi_eok + 31439);
char gih = (char)((int)ga + 587);


char ni_en = 'ㄴ';
char na = (char)((int)gi_eok + 31439 + 588); 
char nih = (char)((int)na + 587);

char di_geut = 'ㄷ';
char da = (char)((int)gi_eok + 31439 + 588*2); 
char dih = (char)((int)da + 587);

 

이 규칙을 토대로 초성일때는 초성이 들어가는 글자에서 모두 true를 리턴할 수 있도록 함수를 작성하였습니다.

bool IsChosung2(char OriginWordChar, char keywordChar)
{
    List<char> chosungs = new() { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };
    if (chosungs.Contains(keywordChar))
    {
        int startCharacter = (int)'ㄱ' + 31439 + 588 * chosungs.IndexOf(keywordChar);
        int endCharacter = startCharacter + 587;
        return startCharacter <= OriginWordChar && OriginWordChar <= endCharacter;
    }
    else
    {
        return OriginWordChar == keywordChar;
    }
}


///////////////////////////////////////////////////
// 참고로 지난 글에서 이부분의 함수는 아래와 같습니다.
bool IsChosung1(char OriginWordChar, char keywordChar) => keywordChar switch
{
    'ㄱ' => '가' <= OriginWordChar && OriginWordChar <= '깋',
    'ㄴ' => '나' <= OriginWordChar && OriginWordChar <= '닣',
    'ㄷ' => '다' <= OriginWordChar && OriginWordChar <= '딯',
    'ㄹ' => '라' <= OriginWordChar && OriginWordChar <= '맇',
    'ㅁ' => '마' <= OriginWordChar && OriginWordChar <= '밓',
    'ㅂ' => '바' <= OriginWordChar && OriginWordChar <= '빟',
    'ㅅ' => '사' <= OriginWordChar && OriginWordChar <= '싷',
    'ㅇ' => '아' <= OriginWordChar && OriginWordChar <= '잏',
    'ㅈ' => '자' <= OriginWordChar && OriginWordChar <= '짛',
    'ㅊ' => '차' <= OriginWordChar && OriginWordChar <= '칳',
    'ㅋ' => '카' <= OriginWordChar && OriginWordChar <= '킿',
    'ㅌ' => '타' <= OriginWordChar && OriginWordChar <= '팋',
    'ㅍ' => '파' <= OriginWordChar && OriginWordChar <= '핗',
    'ㅎ' => '하' <= OriginWordChar && OriginWordChar <= '힣',
    'ㄲ' => '까' <= OriginWordChar && OriginWordChar <= '낗',
    'ㄸ' => '따' <= OriginWordChar && OriginWordChar <= '띻',
    'ㅃ' => '빠' <= OriginWordChar && OriginWordChar <= '삫',
    'ㅆ' => '싸' <= OriginWordChar && OriginWordChar <= '앃',
    'ㅉ' => '짜' <= OriginWordChar && OriginWordChar <= '찧',
    _ => keywordChar == OriginWordChar
};

 

 

지난 글에서 작성한 부분 중 뒷부분에 대한 설명이라 앞부분은 그냥 가져왔습니다.

방금 작성한 IsChosung2()가 아닌 다른 부분이 궁금하시다면

지난글인 "[C#] 초성을 포함한 StartWith 함수 만들기" (링크: https://pichen.tistory.com/56 ) 를 참고해주세요!

//초성 검색 테스트
string keyword1 = "ㄱ나ㄷ";
string keyword2 = "aㄱ나";
List<string> words = new List<string> { "가나다", "가노", "가난", "가나두", "가나라", "고리", "기체", "깋체", "나다라", "다라마", "가나a", "a가나", "a가나다" };
List<string> list1 = words.FindAll(w => StartWithKeyword(keyword1, w));
List<string> list2 = words.FindAll(w => StartWithKeyword(keyword2, w));
Print(keyword1, list1);
Print(keyword2, list2);



void Print(string keyword, List<string> papers) => papers.ForEach(p => Console.WriteLine($"검색어: {keyword} - {p}"));

bool StartWithKeyword(string keyword, string word)
{
    if (keyword.Length > word.Length)
    {
        return false;
    }

    for (var i = 0; i < word.Length && i < keyword.Length; i++)
    {
        //bool charResult = IsChosung1(word[i], keyword[i]);
        bool charResult = IsChosung2(word[i], keyword[i]);
        if (!charResult)
        {
            return false;
        }
    }
    return true;
}



bool IsChosung2(char OriginWordChar, char keywordChar)
{
    List<char> chosungs = new() { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };
    if (chosungs.Contains(keywordChar))
    {
        int startCharacter = (int)'ㄱ' + 31439 + 588 * chosungs.IndexOf(keywordChar);
        int endCharacter = startCharacter + 587;
        return startCharacter <= OriginWordChar && OriginWordChar <= endCharacter;
    }
    else
    {
        return OriginWordChar == keywordChar;
    }
}

 

IsChosung함수의 길이는 짧아졌는데, 성능도 과연 그럴까?

정답은 아닙니다.

먼저 방금 작성된 함수는 숫자를 매번계산해야 합니다. => 따라서 성능을 개선하기 위해서는 변화하는 값이 아니기 때문에 미리 계산하거나 하드코딩으로 클래스로 초성, 시작글자, 마지막글자의 int나 char값을 작성 후 컬렉션에 넣어서 관리해야 첫번째 함수와 유사한 퍼포먼스를 낼 수 있을 것입니다.

 

그러나 이러한 개발과정과 검토가 개발실력에 많은 도움이 될 것이라 믿습니다. 감사합니다.

반응형
반응형

개발자 중 누군가 초성을 포함하여 검색하는 방법을 물었는데, 시간이 남아서 개발을 했다가 초안이 맘에 안들어서 퇴근 후 공부를 마친 시간에 보완작업을 해보았습니다.

 

현재 다니는 회사에서 사용하는 언어가 C#인데, 회사에 적용해도 좋을 것 같아서 C#을 사용했습니다.

 

일단 전체 검색대상 목록을 List<string>에 담았다고 가정하여, 

다음과 같은 List를 선언합니다.

List<string> words = new List<string> { "가나다", "가노", "가난", "가나두", "가나라", "고리", "기체", "깋체", "나다라", "다라마", "가나a", "a가나", "a가나다" };

 

그리고 검색어를 전체 구현이라면 앞단에서 받아야 겠지만, 콘솔을 통한 입력도 받지 않고, 선언을 하겠습니다.

 

검색어는 

string keyword1 = "ㄱ나ㄷ";
string keyword2 = "aㄱ나";

이렇게 두개로 하겠습니다.

 

컬렉션에서 어떤 조건을 만족하는 모든 값을 컬렉션으로 반환하는 함수인 FindAll()을 사용하도록 할텐데요,

words.FindAll(w => 어떤함수(w))로 사용할 어떤함수를 작성해야겠죠.

 

words.FindAll(word => 어떤함수( word )) 는 아래와 같은 기능을 한다고 이해하지면 됩니다. 실제로는 변수에 델리게이트를 사용하는 것이지만, 이해를 돕기 위해 작성한 것입니다.

List<string> 가짜FindAll(List<string> words){
    List<string> a = new();
    foreach(string word in words) 
    {
         if(어떤함수(word))
             a.Add(word)
    }
    return a;
}

 

그러면 어떻게 해야 초성에 맞는 한글들을 표현해낼 것인가를 고민해야겠죠.

 

우리는 char라는 자료형을 알고있죠. 유니코드로 숫자 범위로서 한글에 대한 범위를 지정해볼 수 있습니다.

따라서 검색어 중 어떤 글자가 'ㄱ', 'ㄴ' 등의 자음 초성일때, 가~깋, 나 ~닣가 포함되어있는지를 범위로 지정해볼 생각을 할 수 있습니다.

 

간단하게 검색어와 실제단어를 비교한다고 생각하고, n번째 글자가 ㄱ이면 원래 글자가 '가'와 '깋' 사이에 있는지 여부를 반환합니다.

if(검색어 중 n번째 글자 == 'ㄱ') {

  	원래 글자 중 n번째 글자 >= '가' && 원래 글자 중 n번째 글자 <= '깋'

}

 

받침 등이 자음마다 달라서 아주 일정한 규칙이 있는 것이 아니라서 이 작업을 ㄱ~ㅎ까지 모두 해주어야 합니다.

엑셀을 이용해도 됩니다.

 

그런데 우리는 switch 변수구문을 작성한 적이 있기 때문에 아래와 같이 작성합니다.

저 같은 경우는 ㄱ~ㅎ, 가~하, 깋~힣은 gpt에게 한글을 작성해달라고 하고, 엑셀에서 목록을 만들어서 다시 vs에 붙여넣기 했답니다. 초성이 아닌 경우에는 검색어와 원래 단어의 n번째 글자가 같으면 되기 때문에 _ => unicode == OriginChar로 작성하였습니다. OriginChar는 원래 목록에 있던 단어의 n번째 글자이고 unicode는 검색어의 n번째 글자입니다. 지금보니 변수명을 잘못지었지만, 이미 개발했으니 그대로 쓰겠습니다.

 

bool IsChosung(char OriginChar, char unicode) => unicode switch 
{
    'ㄱ' => '가' <= OriginChar && OriginChar <= '깋',
    'ㄴ' => '나' <= OriginChar && OriginChar <= '닣',
    'ㄷ' => '다' <= OriginChar && OriginChar <= '딯',
    'ㄹ' => '라' <= OriginChar && OriginChar <= '맇',
    'ㅁ' => '마' <= OriginChar && OriginChar <= '밓',
    'ㅂ' => '바' <= OriginChar && OriginChar <= '빟',
    'ㅅ' => '사' <= OriginChar && OriginChar <= '싷',
    'ㅇ' => '아' <= OriginChar && OriginChar <= '잏',
    'ㅈ' => '자' <= OriginChar && OriginChar <= '짛',
    'ㅊ' => '차' <= OriginChar && OriginChar <= '칳',
    'ㅋ' => '카' <= OriginChar && OriginChar <= '킿',
    'ㅌ' => '타' <= OriginChar && OriginChar <= '팋',
    'ㅍ' => '파' <= OriginChar && OriginChar <= '핗',
    'ㅎ' => '하' <= OriginChar && OriginChar <= '힣',
    'ㄲ' => '까' <= OriginChar && OriginChar <= '낗',
    'ㄸ' => '따' <= OriginChar && OriginChar <= '띻',
    'ㅃ' => '빠' <= OriginChar && OriginChar <= '삫',
    'ㅆ' => '싸' <= OriginChar && OriginChar <= '앃',
    'ㅉ' => '짜' <= OriginChar && OriginChar <= '찧',
    _ => unicode == OriginChar
};

 

이 작업을 검색어와 단어목록의 단어중 하나에서 글자단위로 진행해야하므로, foreach(char a in string값) 또는 for문에 대입하겠습니다.

 

여기서는 검색어와 단어를 index단위로 비교해야하므로, for문이 적절하고, 둘 중 짧은 곳에 맞추어 돌려야 하므로 조건에 둘다 작성합니다.

 

IsChosung의 값이 false이면 더이상 뒷부분을 확인하지 않아도 되므로, false를 반환하고 루프를 끝냅니다.

bool 어떤함수?(string keyword, string word){
    for (var i = 0; i < word.Length && i < keyword.Length; i++)
    {
        bool charResult = IsChosung(word[i], keyword[i]);
        if (!charResult)
        {
            return false;
        }
    }
    return true;
}

여기까지만 하면 문제가 발생하는데 찾으셨나요?

 

바로 검색어가 단어보다 길어도 앞부분까지만 매칭이 되면 true를 반환하는 문제가 생기죠.

 

그래서 앞에 길이를 비교하는 조건이 필요합니다.

bool 어떤함수?(string keyword, string word){
	if (keyword.Length > word.Length)
    {
        return false;
    }
    
    for (var i = 0; i < word.Length && i < keyword.Length; i++)
    {
        bool charResult = IsChosung(word[i], keyword[i]);
        if (!charResult)
        {
            return false;
        }
    }
    return true;
}

 

이제 출력의 시간입니다.

독자분들의 편의를 위해서 앞서 작성한 부분들도 기입하겠습니다.

 

string keyword1 = "ㄱ나ㄷ";
string keyword2 = "aㄱ나";
List<string> words = new List<string> { "가나다", "가노", "가난", "가나두", "가나라", "고리", "기체", "깋체", "나다라", "다라마", "가나a", "a가나", "a가나다" };
List<string> list1 = words.FindAll(w => StartWithKeyword(keyword1, w));
List<string> list2 = words.FindAll(w => StartWithKeyword(keyword2, w));
Print(keyword1, list1);
Print(keyword2, list2);


// foreach console.writeline 두번안쓰려고 선언한 함수
void Print(string keyword, List<string> papers) => papers.ForEach(p => Console.WriteLine($"검색어: {keyword} - {p}"));

bool StartWithKeyword(string keyword, string word)
{   
    if (keyword.Length > word.Length)
    {
        return false;
    }

    for (var i = 0; i < word.Length && i < keyword.Length; i++)
    {
        bool charResult = IsChosung(word[i], keyword[i]);
        if (!charResult)
        {
            return false;
        }
    }
    return true;
}

bool IsChosung(char OriginChar, char unicode) => unicode switch 
{
    'ㄱ' => '가' <= OriginChar && OriginChar <= '깋',
    'ㄴ' => '나' <= OriginChar && OriginChar <= '닣',
    'ㄷ' => '다' <= OriginChar && OriginChar <= '딯',
    'ㄹ' => '라' <= OriginChar && OriginChar <= '맇',
    'ㅁ' => '마' <= OriginChar && OriginChar <= '밓',
    'ㅂ' => '바' <= OriginChar && OriginChar <= '빟',
    'ㅅ' => '사' <= OriginChar && OriginChar <= '싷',
    'ㅇ' => '아' <= OriginChar && OriginChar <= '잏',
    'ㅈ' => '자' <= OriginChar && OriginChar <= '짛',
    'ㅊ' => '차' <= OriginChar && OriginChar <= '칳',
    'ㅋ' => '카' <= OriginChar && OriginChar <= '킿',
    'ㅌ' => '타' <= OriginChar && OriginChar <= '팋',
    'ㅍ' => '파' <= OriginChar && OriginChar <= '핗',
    'ㅎ' => '하' <= OriginChar && OriginChar <= '힣',
    'ㄲ' => '까' <= OriginChar && OriginChar <= '낗',
    'ㄸ' => '따' <= OriginChar && OriginChar <= '띻',
    'ㅃ' => '빠' <= OriginChar && OriginChar <= '삫',
    'ㅆ' => '싸' <= OriginChar && OriginChar <= '앃',
    'ㅉ' => '짜' <= OriginChar && OriginChar <= '찧',
    _ => unicode == OriginChar
};

 

결과는 다음과 같습니다.

 

영어를 포함해도 뒤에 초성이 바르게 검색되는 것을 확인했습니다.

 

 

 

반복되는 형태의 문장들인데 계산식으로 만들 수 없을까? 궁금하신 분들은 2편을 참고해주세요.

https://pichen.tistory.com/57

 

[C#] 초성을 포함한 StartWith 함수 만들기 2편, char의 유니코드 값으로 계산해보자.

"[C#] 초성을 포함한 StartWith 함수 만들기" (링크: https://pichen.tistory.com/56 ) 글에서 ㄱ부터 ㅎ까지를 모두 직접 적어서 switch 돌리는 것에 계산식이 있지 않겠냐고 하는 반응을 보았습니다. [C#] 초성

pichen.tistory.com

 

 

반응형
반응형

파일을 읽는 방법은 여러가지가 있다. 필자가 최근에 가장 많이 사용하고 있는 방법은 커스터마이징 한 것으로 다른 글에서 잠깐 다뤄 볼 예정이고, 

 

본문에서는 .json 파일과 .ini 파일을 읽는 방법에 대해 알아보자.

 

 

 

먼저 [.ini 파일을 읽는 방법]

 

 

아래와 같은 Location.ini 파일을 바탕화면에 생성한다.

[Lo_1]
name=Jeju
stationID=A
PID=1234

[Lo_2]
name=Busan
stationID=2
PID=7654

 

 

ini 파일은 아주 구시대적일 수 있는 정보파일로 보통은 시작할때 읽어들이는 정보를 담은 파일을 뜻한다.

 

using System.Runtime.InteropServices; 라이브러리를 사용하여

kernel32.dll을 import하고, 그중에서도 GetPrivateProfileString()을 사용한다.

 

이를 사용하기 위한 세팅은 다음과 같다.

using System.Runtime.InteropServices;

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern uint GetPrivateProfileString(
        string lpAppName,
        string lpKeyName,
        string lpDefault,
        StringBuilder lpReturnedString,
        uint nSize,
        string lpFileName
);

 

 

가져올때에는 ini 파일내 변수들을 List나 Dictionary에 넣어두는 것이 루프문을 통해 읽어올 수 있어서 편하다.

 

List<string> iniKeys = new ()
{
	"name",  "stationID", "PID"
};

 

 

 

설정파일은 바탕화면에 있는 것으로 가정하였으므로 파일경로는 

Environment.GetFolderPath를 사용해도 좋다.

string iniPath = Path.Combine(
	Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
    "Location.ini"
);

Path.Combine()은 파일경로를 \가 없으면 붙이고 있으면 그대로 잇는 등

자동으로 파일경로를 결합시켜주는 역할을 한다.

 

Environment.GetFolderPath()는 운영체제 환경에서 특정 폴더경로를 가져오는 함수로

Environment.SpecialFolder 내에 바탕화면(Desktop), 즐겨찾기, appdata 등 여러 경로가 존재하고 저는 그 중에서 바탕화면을 썼던 것이다.

 

 

해당 ini파일이 존재하면 읽어서 출력해보자.

만일 없는 변수가 있으면 Warning으로 보이게 하면 구분이 쉽다.

 

아래와 같은 방법 말고도 특정 변수에 집어 넣어야만 확실한 사용이 될 것인데, for문의 i와 foreach의 key 값에 따라 변수에 집어 넣으면 된다.

Log.Information("iniPath:" + iniPath);

if (File.Exists(iniPath))
{
    for (int i = 1; i <= 2; i++)
    {
        StringBuilder sb = new StringBuilder(255);
        foreach (var key in iniKeys)
        {
            uint result = GetPrivateProfileString($"Lo_{i}", key, "", sb, (uint)sb.Capacity, iniPath);
            if (result > 0)
            {
                Log.Information("Value: " + sb.ToString());
            }
            else
            {
                Log.Warning("Value: " + sb.ToString());
            }
        }
    }
}

 

 

kernel32.dll의 GetPrivateProfileString() 함수는

1. ini 파일 내 [와 ] 사이값

2. ini 파일 내  [] 바로 아래 변수명 값

3. ini 파일 내  읽을 변수에 값이 없을때 대체 값

4. StringBuilder 변수값

5. 글자의 길이 제한

6. ini파일의 경로

 

로 구성되어있다.

 

 

 

(참고) 제 포스팅을 읽으실때 Log.Information()을 사용하지 못하시는분은 없겠죠?

만일 있다면 Serilog 포스팅도 읽어보기를 추천한다.

 

다만 부수적이라 생각하고 계속 진행할 독자라면 여기서 사용한 방법만 간단하게 설명할테니 따라하면 된다. 

nuget 패키지로 Serilog, Serilog.Sink.File을 설치한 후 

 

소스 최상단에 아래 내용을 추가하면된다. Serilog에 대해서는 자세한 설명은 하지 않겠다.

using Serilog;

string appName = "only_for_test";

Log.Logger = new LoggerConfiguration()
    // .WriteTo.Console()
    .WriteTo.File(
        $"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}/{appName}_Logs/{appName}_log-.txt",
        rollingInterval: RollingInterval.Day,
        retainedFileCountLimit: null,
        fileSizeLimitBytes: 50 * 1024 * 1024,
        rollOnFileSizeLimit: true,
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}")
    .MinimumLevel.Is(Serilog.Events.LogEventLevel.Information)
    .CreateLogger();

 

 

 

 

이번엔 [.json 파일을 읽는 방법]

 

json을 읽는 방법은 아주 간단하다.

우선 바탕화면에 Location.json 파일이 다음과 같다고 하자.

ini 파일과 준위를 맞추고자 아래 처럼 작성하였다.

{ 
	"Lo_1": {
		"name":"incheon", 
		"stationID": "A", 
		"PID": "1234"
	},
	"Lo_2": {
		"name":"busan", 
		"stationID": "1", 
		"PID": "1256"
	}
}

 

파일 경로는 다음과 같이 가져온다.

string jsonPath = Path.Combine(
	Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
    "Location.json"
);

 

 

File.ReadAllText(string filePath) 로 파일내의 모든 문자를 받아와

System.Text.Json 라이브러리에 속한 json을 클래스로 Deserialize 해준다.

 

Deseiralize를 제네릭으로 바로 인스턴스로 만들 수 있도록, .json 정보에 맞게 클래스를 선언해준다.(아래)

그러면 소스 내에서 바로 사용이 가능하다.

 

if (File.Exists(jsonPath))
{
    try
    {
        // 파일의 내용을 문자열로 읽어옴
        string jsonString = File.ReadAllText(jsonPath) ?? "";
        var Locations = JsonSerializer.Deserialize<Dictionary<string, Location>>(jsonString);
        if (Locations is not null)
        {
            foreach(var key in Locations.Keys)
            {
                Log.Information($"{key}= {Locations[key]}");
                if (Locations[key] is not null)
                {
                    Log.Information($"{key}=> name: {Locations[key].Name}");
                    Log.Information($"{key}=> stationID: {Locations[key].StationId}");
                    Log.Information($"{key}=> PID: {Locations[key].Pid}");
                }         
            }  
        }
    }
    catch (Exception e)
    {
        Log.Error($"{e}");
    }
}


class Location
{
    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("stationID")]
    public string? StationId { get; set; }

    [JsonPropertyName("PID")]
    public string? Pid { get; set; }
}

 

 

설정파일을 읽는 방법들에 대해서 다뤄봤다. xml은 있는 것을 유지보수 할때 말고 직접 개발을 초기부터할때는 사용하지 않아서 다루지 않았다.

반응형
반응형

 

윈도우11의 기본배경화면

 

 

 

[한글 Windows 10의 특징]

 

1. GUI 사용(Graphic User Interface)

  - 키보드로 명령어를 직접 입력하지 않고, 아이콘이나 메뉴를 마우스 등으로 선택하여 작업을 수행하는 작업환경

  - 초보자도 쉽게 사용할 수 있다.

 

2. 선점형 멀티태스킹(Preemptive Multi-tasking) 

  - 운영체제가 각 작업의 CPU 이용시간을 제어하여 앱실행 중 문제가 발생하면 해당 앱을 강제 종료, 모든 시스템 자원을 반환하는 멀티태스킹 방식

  - 하나의 앱이 CPU를 독점하는 것을 방지하여 시스템 다운 현상없이 안정적 작업 가능

 

3. 플러그 앤 플레이(Plug & Play)

  - 컴퓨터가 시스템에 하드웨어를 설치했을 떄, 해당 하드 웨어를 사용하는데 필요한 시스템 환경을 운영체제가 자동으로 구성해 주는 것

  - 하드웨어와 소프트웨어 모두 플러그 앤 플레이를 지원하여야 가능하다.

  - 하드웨어의 IRQ, DMA 채널, I/O 주소 들이 충돌하지 않도록 설정한다.

ex) 마우스나 usb를 포트 연결하면 자동으로 인식하고 사용할 수 있는 것

 

4. OLE(Object Linking and Embedding) 

 - 다른 여러 앱에서 작성된 문자나 그림 등의 개체를 현재 작성 중인 문서에서 연결(Linking)하거나 삽입(Embedding)하여 편집할 수 있게 하는 기능

 - OLE로 연결된 이미지 원본 앱에서 수정 시 해당문서에도 반영됨

 

5. 255자까지 파일이름 설정가능

 - 파일이름은 255자까지 가능

 - NTFS에서는 유니코드 문자를 지원하여 세계 여러 문자를 파일이름에 사용가능

 - 파일 이름에 \ / : * ? " < > |를 제외한 모든 문자 및 공백을 사용할 수있다.

    : 경로나 태그 등에 쓰이는 특수문자가 안되는 걸로 생각하고 외웠다.

 

6. 64bit 데이터 처리

 - 32비트보다 더 많은 양의 데이터를 빠르게 처리할 수 있으며 사용자에게 빠르고 효율적인 시스템을 구축

    : 참고로 운영체제가 64비트면 사용할 어플리케이션도 64비트 호환되는 앱을 사용해야하고,

      32비트 앱을 64비트 버전의 Windows에서 실행하면 동작하지 않는다. 마찬가지로 64비트 앱을 32비트 버전의 Windows에서 실행하면 동작하지 않는다.

------------------------------------------------------------------------------------------------------------------------------------------------------------

 

[파일 시스템]

 : 보조기억장치에 저장되는 파일에 대해 수정, 삭제, 추가, 검색 등의 작업을 지원하는 관리 시스템

   -> 아래 파일 시스템의 종류는 WIndows에서만 사용가능

 

 - FAT : MS-DOS 및 기타 Windows 기반의 운영체제에서 파일을 구성할 떄 사용되는 시스템

   # 파티션 용량은 2GB제한

 

 - FAT32 : FAT에서 파생되어 보다 큰 드라이브를 사용할 수 있고 보다 클러스터가 작아 하드디스크 공간의 낭비를 줄일 수 있다.

 

 - NTFS: 성능, 보안, 디스크 할당, 앉어성, 속도 면에서 FAT, FAT32보다 뛰어난 기능 제공, 시스템 리소스 사용 최소화

    # 파일 및 폴더에 대한 엑세스 제어를 유지

    # 제한된 계정을 지원

    # 최대 볼륨의 크기: 256TB

    # 파일크기는 볼륨의 크기에 의해서만 제한

    # 비교적 큰 오버헤드가 있어 400MB이상의 볼륨에서 효과적

 

반응형
반응형

https를 사용하려면 필요한 ssl인증서, 서비스할 페이지도 아니라서 유료결제는 부담되시죠?

 

openssl을 통해 무료로 ssl인증서를 발급받는방법에 대해 설명드리겠습니다.

 

참고로 유효기간이 존재하고 기본적으로 한달입니다. 장기적으로 사용하려면 설정을 추가하거나 매월 발급받으셔야합니다. 

 

먼저 openssl을 설치해야겠죠,

 

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions (slproweb.com)

 

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

Minimum system requirements: Windows XP or later 32MB RAM 200MHz CPU 30MB hard drive space Recommended system requirements: Windows XP or later 128MB RAM 500MHz CPU 300MB hard drive space March 14, 2023 - OpenSSL 3.1 is available. Also, Happy Pi Day. Users

slproweb.com

위 링크로 들어가 적절한 openssl을 다운받고 설치합니다.(MSI를 권장합니다.)

 

그 뒤 환경변수를 설정해야합니다.

 

환경변수를 설정하는 곳으로 진입하는 방법은

 

1. 개발자스러운 방법: 시작 버튼 우클릭(win + R) -> sysdm.cpl -> 고급 탭 -> 환경변수

 

2. 탐색기를 실행 -> 내 PC우클릭 -> 속성 -> 시스템 탭(이미 열려있음) -> [장치 사양] 아래 [관련링크] 내 [고급 시스템 설정] -> 고급탭 -> 환경변수

내 pc를 우클릭한 화면

 

 

하단 [시스템 변수]에 있는 [Path]를 편집-> [새로만들기] -> openssl 설치폴더의 경로 내 bin폴더 위치를 입력

 

(그 위치 경로는 대게 C:\Program Files\OpenSSL-Win64\bin 형식인데 독자의 방문시기나 다운 버전 등에 따라 달라질 수 있음!)

 

 

[[[[[[[[[[[[[ 발급 받아보자! ]]]]]]]]]]]]]]]

이제 명령프롬프트 창을 엽니다.

 

 1. 개인키 생성 : win + R -> cmd -> Enter -> cd [경로] (적당히 발급할 파일을 위치할 경로로 이동) -> 아래 명령어를 치고 

openssl genrsa -out [키파일명].key 2048

 

2. 인증서 신청파일 생성: 아래 명령어 치기

openssl req -new -key [위에서생성한키파일명].key -out [인증서신청파일명].csr

치고나면 위 캡처처럼 국가 명, 지역명, 위치 등 적으라고 나오는데 임시 테스트용도로나 발급받을만한 무료발급이기 때문에 적당히 적고싶은 걸 적으면된다. 다만 Common Name에 사용할 서버 도메인을 입력해야한다고 한다.

 

3. 인증서를 생성

openssl x509 -req -in [위 발급 csr파일명].csr -signkey [위 발급 key파일명].key -out [발급받을인증서파일명].crt

여기서 끝나실 분들도 있을 것이지만 iis에 올리기 위해 발급받으신다면 한 단계가 더 남아있습니다.

 

 

4. pfx파일로 변환 : iis에서는 pfx 확장자를 받을 수 있기 때문에 변환을 해주어야 합니다.

openssl pkcs12 -export -in [위 crt파일].crt -inkey [위 key파일].key -out [생성할파일명].pfx

이제 서버에 이 인증서를 설치하면 사용할 수 있습니다.

반응형

+ Recent posts