반응형

윈도우10의 WSL(Windows Subsystem for Linux. 리눅스용 윈도우 하위 시스템)을 버전 2로 올리는 방법 입니다.

저의 경우 WSL으로 Ubuntu가 설치되어 있어서 이를 기준으로 설명 합니다.

WSL 1과 2 차이점은 다음과 같습니다.

기능 WSL1 WSL2
Windows와 Linux 통합
빠른 부팅 시간
작은 리소스 공간
현재 버전의 VMware 및 VirtualBox에서 실행
관리 VM ×
전체 Linux 커널 ×
전체 시스템 호출 호환성 ×
OS 파일 시스템 간 성능 ×

특별히 윈도우와 WSL간에 파일 시스템을 공유할 필요가 없으면 WSL2를 사용하는 것이 성능과 Linux 호환성 측면에서 좋을 것 같습니다.

WSL 버전 확인

기존에 설치된 WSL의 버전을 확인 합니다.

wsl -l -v

WSL 기능 활성화

PowerShell에서 다음 명령을 실행 합니다 (단 PowerShell는 관리자 모드로 실행 되어야 합니다).

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

VM기능 활성화

이어서 다음 명령을 실행 합니다.

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

WSL2로 변환

다음 명령으로 WSL2로 변환작업을 진행 합니다. 시간이 약간 걸립니다.

wsl --set-version ubuntu 2

제대로 변환되었는지 확인하기 위해 버전을 다시 확인 해 봅니다.

사실 제 경우에는 WSL2로 변환 시 다음과 같은 오류가 발생 하였습니다.

가상 컴퓨터 플랫폼 Windows 기능을 사용하도록 설정하고 BIOS에서 가상화가 사용하도록 설정되어 있는지 확인하세요.

BIOS도 확인 했고 dism.exe 명령도 이상 없이 실행 했는데도 계속 오류가 발생 하였는데, 결국은 윈도우즈 업데이트를 하니까 해결이 되었습니다.

반응형

윈도우즈 터미널(Windows Terminal)은 윈도우 10에서 새로이 제공되는 CUI (Character User Interface) 환경입니다.

유니코드 및 UTF-8을 사용하기 때문에 다양한 언어의 이모지 및 문자 표시가 가능하고 GPU를 사용하여 텍스트를 렌더링 하므로 기존 환경보다 성능이 향상되었다고 합니다.

명령 프롬프트와 Power shell 그리고 WSL (Ubuntu 등 Windows Subsystem for Linux)을 하나의 프로그램에서 멀티탭으로 관리할 수 있고, 하나의 화면을 횡으로 종으로 분할하는 것도 가능합니다.

윈도우즈 터미널을 설치하기 위해서는 윈도우 10 버전이 2004로 업데이트 되어 있어야 합니다.

설치는 Microsoft store에서 하면 되는데 terminal로 검색하면 쉽게 찾을 수 있습니다.

 

받기를 누르면 별다른 과정 없이 곧바로 설치가 진행됩니다.

Windows 터미널을 실행하면 기본적으로 Power Shell 모드로 실행됩니다.

상단의 역 캐럿 모양을 클릭하면 명령 프롬프트, Ubuntu, Azure Cloud Shell을 새 탭으로 실행할 수 있습니다.

Alt+Shift+D 단축키를 눌러서 한 화면을 가로나 세로로 분할이 가능합니다.

설정 메뉴를 선택하면 settrings.json을 편집하는 창이 열리는데 여기서 단축키, 색상 등을 커스텀화 할 수 있습니다.

과거 IE만을 고집하며 호환성과 표준을 무시하던 마이크로소프트가 점점 오픈소스 생태계의 일원으로 개발자와 사용자를 위한 진정한 개발회사로 거듭나고 있는 것 같아서 다음 행보가 또 기대가 됩니다.

반응형

2013년에 포스팅한 내용이 너무 간단한 것 같아서 좀 더 자세한 내용으로 다시 올립니다.

hackhyun.tistory.com/231

 

[SQL서버] PK(Primary Key) 변경 하기

SQL서버에서 기존의 PK를 다른 컬럼 구성으로 변경 하려면 다음과 같이 한다. -- 기존 PK명 확인 sp_help 테이블명 --기존 PK 삭제 ALTER TABLE 테이블명 DROP CONSTRAINT 기존_PK명 --새로운 PK 정의 ALTER TABLE..

hackhyun.tistory.com

SQL서버에서 기존의 PK로 사용된 컬럼을 다른 컬럼으로 변경하려면 다음과 같이 합니다.

1. 기존 PK 명(인덱스 명) 확인

sp_helpindex 테이블명
  • primary key로 지정된 인덱스 명을 확인 합니다.

2. 기존 PK 삭제

ALTER TABLE 테이블명
DROP CONSTRAINT 기존_PK명
  • 1에서 확인된 인덱스 명을 이용합니다.

3. 새로운 PK 정의

ALTER TABLE 테이블명
ADD CONSTRAINT 새_PK명 
PRIMARY KEY [NONCLUSTERED](컬럼1, 컬럼2, ...)
  • 새 PK에 포함될 컬럼은 NULL이 허용되지 않는 컬럼이어야 합니다.
  • 별도로 지정하지 않으면 PK 컬럼이 클러스터 인덱스로 생성됩니다.
  • PK가 아닌 다른 컬럼을 클러스터 인덱스로 사용할 계획이라면 NONCLUSTERED 옵션을 지정합니다.

4. 잘 변경되었는지 확인

 

반응형

■ Visual Studio 2010, SQL Server 2014 기준으로 개발

SQL 서버에 OpenAPI 호출하는 CLR을 등록하여, 거래처가 특정 조건을 만족하는지 검사하는 기능을 개발하게 되었는데 이 과정의 일부를 남깁니다.

CLR 프로젝트 생성

우선 Visual Studio에서 다음과 같이 SQL Server > Visual C# SQL CLR 데이터베이스 프로젝트를 시작합니다.

개발할 CLR이 SQL Sever에서 참조할 구성요소를 선택할 필요가 없으면 데이터베이스 참조는 취소해도 됩니다.

저의 경우 JSON을 처리하는 다른 DLL의 참조가 필요하여 DB 연결 정보를 입력하였습니다.
만약 연결된 DB 정보가 없이 참조 추가하려고 하면 다음과 같이 추가가 불가능합니다.

Visual Studio에서 기본적으로 생성되는 프로젝트 구성은 다음과 같습니다.

클래스 파일 추가

이제 실제 로직을 C#으로 구현하기 위하여 새로운 클래스 파일을 추가합니다 (이름은 NIMS.cs로 하였습니다).

추가된 빈 클래스 파일의 모습입니다.

API 스펙 확인

사용하려는 OpenAPI의 스펙은 다음과 같습니다.

JSON for. NET

위 OpenAPI의 호출 결과는 JSON으로 전달되기 때문에 JSON을 파싱 하기 위하여 JSON for. NET이라는 오픈 라이브러리를 사용합니다.

다음 사이트에서 파일을 내려받습니다.
sourceforge.net/projects/csjson/

내려받은 json-for-dotnet-1.2.zip 파일의 압축을 풀어서 System.Net.Json.dll 파일을 SQL 서버에 복사합니다.

제가 만든 CLR에서 이 System.Net.Json.dll을 참조해야 하므로 다음과 같이 SQL 서버에 어셈블리 등록을 합니다.

EXEC SP_CONFIGURE 'clr enabled', 1
RECONFIGURE WITH OVERRIDE 

ALTER DATABASE [MyDB] SET 
TRUSTWORTHY ON

CREATE ASSEMBLY Json
FROM 'D:\System.Net.Json.dll'    
WITH PERMISSION_SET = UNSAFE

그런데 JSON for. NET에서 제공해주는 기본 파서 기능이 좀 빈약하여, OpenAPI 결과를 처리할 수 있도록 기능 보완된 파서를 만들기 위하여 클래스 파일을 하나 더 추가합니다 (이름은 MyJsonParser.cs으로 했습니다).

그리고 다음과 같이 로직을 작성하였습니다.

MyJsonParser.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Json;

namespace NIMSAPI
{
    class MyJsonParser
    {
        private JsonTextParser parser;
        private JsonObjectCollection joc;

        public MyJsonParser(string planText = "")
        {
            parser = new JsonTextParser();
            if (!string.IsNullOrEmpty(planText))
            {
                joc = (JsonObjectCollection)parser.Parse(planText);
            }
        }

        // 생성자에서 주어진 초기 문자열 외에 추가적으로 파싱할 때 사용
        public void Parse(string planText)
        {
            planText = planText.Replace("\r", "").Replace("\n", "").Replace("\t", "");    // 파싱 오류방지를 위해 tab 및 개행문자열 제거

            joc = (JsonObjectCollection)parser.Parse(planText);
        }

        public object GetValue(string namePath, char split = '/', bool returnEmptyWhenNull = true)
        {
            string[] names = namePath.Split(split);
            JsonObjectCollection obj = null;

            if (names.Length == 1)
            {
                if (joc[namePath] is JsonObjectCollection)
                    obj = (JsonObjectCollection)joc[namePath];

                if (joc[namePath] is JsonArrayCollection)
                    obj = joc;
            }
            else
            {
                for (int i = 0; i < names.Length - 1; i++)
                {
                    if (i == 0)
                        obj = (JsonObjectCollection)joc[names[i]];
                    else
                        obj = (JsonObjectCollection)obj[names[i]];
                }
            }

            if (obj != null)
            {
                if (obj[names[names.Length - 1]] is JsonArrayCollection)
                    return obj[names[names.Length - 1]];
                else
                    return obj[names[names.Length - 1]].GetValue();
            }
            else
            {
                object retObj = null;

                if ((joc as JsonObjectCollection)[namePath] != null)
                {
                    retObj = (joc as JsonObjectCollection)[namePath].GetValue();
                }

                if (retObj == null && returnEmptyWhenNull)
                {
                    retObj = string.Empty;
                }

                return retObj;
            }
        }
    }
}

참조 추가

솔루션 탐색기에서 참조 추가를 눌러서 

앞에서 등록한 Json 어셈블리를 선택합니다.

정상적으로 추가되면 다음과 같이 참조 하위에 포함되게 됩니다.

이제 NIMS.cs에 OpenAPI를 호출하고 결과를 MyJsonParser로 파싱 처리하는 소스를 작성하였습니다.

NIMS.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.Net.Json;

namespace NIMSAPI
{
    public class NIMS
    {
        // 상대 마약류취급자식별번호가 존재하는지 체크
        //      BizNo: 매출거래처의 사업자번호
        public static bool IsExistsBsshCode(string BizNo)
        {
            string Url = "https://www.nims.or.kr/api/bsshinfo.do";
            bool result = false;

            try
            {
                WebClient webClient = new WebClient();

                NameValueCollection formData = new NameValueCollection();
                formData["k"] = "XXXXXXXXX"; //인증키
                formData["fg"] = "1";
                formData["pg"] = "1";
                formData["bi"] = BizNo;

                byte[] responseBytes = webClient.UploadValues(Url, "POST", formData);
                string Result = Encoding.UTF8.GetString(responseBytes);

                MyJsonParser myParser = new MyJsonParser(Result);

                string ResultCode = myParser.GetValue("response/header/RESULT_CODE").ToString();
                int CustCount = int.Parse(myParser.GetValue("response/body/TOTAL_COUNT").ToString());

                JsonArrayCollection lists = (JsonArrayCollection)myParser.GetValue("response/body/list"); 

                int count = 0;
                foreach (JsonObjectCollection collection in lists)
                {
                    myParser.Parse(collection.ToString());  // 배열 요소를 다시 파싱 시킨다

                    string OppNarcoticManagerID = myParser.GetValue("BSSH_CD").ToString();        // 상대 마약류취급자식별번호
                    result = !string.IsNullOrEmpty(OppNarcoticManagerID);

                    if (++count == 1) break;    // 동일 거래처가 여러개 조회되는 경우가 있어서 한 번만 처리되게 함
                }
            }
            catch
            {
                result = false;
            }

            return result;
        }
    }

}

F6을 눌러서 빌드합니다.

빌드된 NIMSAPI.dll 파일을 SQL 서버에 복사하고 어셈블리 등록을 합니다.

CREATE ASSEMBLY NIMS
FROM 'D:\NIMSAPI.dll'    
WITH PERMISSION_SET = UNSAFE

참고로 다음과 같이 소유자 SID 문제로 어셈블리 등록이 되지 않는 경우 ALTER AUTHORIZATION로 해당 DB의 소유자를 변경해야 합니다.

/*
master 데이터베이스에 기록된 데이터베이스 소유자 SID가 데이터베이스 'MyDB'에 기록된 
데이터베이스 소유자 SID와 다릅니다. 
ALTER AUTHORIZATION 문을 사용하여 데이터베이스 'MyDB'의 소유자를 다시 설정하여 
이 문제를 해결해야 합니다.
*/

-- 현 소유자 확인
SELECT name AS [Database],
       SUSER_SNAME(owner_sid) AS [Owner]
FROM sys.databases
WHERE [name] = 'MyDB'

-- 소유자 변경
ALTER AUTHORIZATION ON DATABASE:: MyDB TO sa ;

사용자 함수 생성

등록된 CLR 어셈블리를 SQL 서버에서 함수 형태로 호출할 수 있도록 함수를 생성합니다.

CREATE FUNCTION FN_NIMS_CUST_EXISTS
(
    @p_BizNo NVARCHAR(20)
)
RETURNS BIT AS EXTERNAL NAME NIMS.[NIMSAPI.NIMS].IsExistsBsshCode


테스트

이 글이 도움이 되셨으면 공감(하트)과 댓글을 부탁 드려요!

+ Recent posts