'2017/04'에 해당되는 글 4건

Programming/.NET

이런 시나리오가 있습니다. 소스가 특정 서버에 있고 이 서버에서 해당 폴더를 공유합니다. 클라이언트에서는 이 폴더에 네트워크드라이브로 연결해 원하는 프로젝트를 Visual Studio로 열어서 작업합니다.

 

개발작업을 이런식으로 진행할때 가끔 Visual Studio의 성능자체가 급격히 저하되는 문제가 발생하곤 합니다.

 

이럴때는 우선 웹프로젝트일 경우 Web Essentials같은 확장도구가 원인인지를 살펴봐야 합니다. 의심되는 확장도구를 우선 제거해서 문제가 해결되는지를 살펴보십시오. 이런 문제가 아니라면 다음절차를 따라해 보시기 바랍니다.

 

1. 네트워크 드라이브로 접근하는 해당 폴더에 대한 권한부여

 

caspol.exe -m -ag 1.2 -url file://\\경로/* FullTrust

 

2. 네트워크 어댑터 속성에서 IPv6와 관련된 모든 항목 제거 또는 사용안함 설정

 

 

 

3. 다음 레지스트리값을 0으로 설정

 

HKEY_LOCAL_MACHINE-SYSTEM-CurrentControlSet-Service-Netlogon-Parameters-RequireSignOrSeal

0 0
Programming/.NET

2002년에 1.0발표로 시작된 닷넷 프레임워크(.NET Framework)는 해당 기반으로 만들어진 프로그램(C#이나 VB.NET과 같은)을 구동하기 위한 자바 가상 머신과 같은 가상 머신입니다. 가상 머신이라고 해서 Hyper-V나 VMWare같은 개별적인 논리의 머신이 아닌 프로세스로서 존재하는 영역을 의미합니다.

 

이를테면 C#으로 프로그램을 만든뒤 이를 컴파일하면 해당 운영체제하에서 바로 실행할 수 있는 네이티브 실행파일(exe)이나 라이브러리(dll)가 아닌 IL(Intermediate Language)이라고 하는 중간코드를 생성하며 이때 닷넷 프레임워크에 포함된 CLR(Common Language Runtime)을 로드할 수 있는 코드를 컴파일 결과물에 포힘시킵니다.

 

따라서 프로그램을 실행하면 내부적으로 CLR이 로드되고 로드된 CLR이 프로그램의 IL코드를 실행하는 방법으로 프로그램을 동작시키게 됩니다. CLR은 재미있게도 공개된 특정 API를 통해 외부 다른 네이티브프로그램등에서 로드하여 실행하는 것이 가능한데요. 이게 왜 중요하냐면 예를 들어 IIS에서 ASP.NET을 통해 닷넷 기반하에 웹 프로그램을 만들고 구동하는 경우 w3wp.exe라는 웹구동 프로세스가 API를 통해 CLR을 로드함으로서 해당 ASP.NET웹프로그램이 동작할 수 있기 때문입니다. 만약 이러한 기능이 없다면 ASP.NET개발자는 CLR을 로드할 수 있도록 하는 별도의 처리를 구현해야 합니다.

 

닷넷 프레임워크는 이러한 CLR과 함께 프로그램을 만들때 유용하게 써먹을 수 있는 클래스 라이브러리모음도 가지고 있는데 이를 BCL(Base Classes Library)이라고 하며 CLR + BCL이 바로 닷넷 프레임워크에 해당합니다. 실제 C#과 같은 언어를 배울때나 프로그램을 만들때 BCL은 언어에서 가장많이 접촉하는 부분에 해당합니다.

 

.NET Framework는 1.0을 시작으로 2017년 현재 4.6까지 공식버전으로 출시되었습니다. 그런데 2.0, 3.0, 3.5 버전은 CLR입장에서는 같은 버전입니다. 근간이 되는 CLR이 바뀌지 않고 그 위에 BCL이나 WPF처럼 여러 라이브러리가 결합되어 버전업이 되었기 때문입니다. 4.0에 와서 2.0이었던 CLR이 4.0으로 바뀌었는데 이 변화는 C:\Windows\Microsoft.NET\Framework 하위에 버전별로 표시되는 폴더를 보면 알 수 있습니다. 다만 .NET Framework 4.5와 4.6이 출시되면서 덩달아 CLR도 버전업이 되었으나 버전에 따른 폴더가 따로 생성되지 않은 이유는 신규버전에 대한 파일을 따로 생성하기 보다는 기존의 4.0을 완전히 갈아엎는 식으로 설치되기 때문입니다. 따라서 'v4.0.30319' 폴더만 존재한다고 하더라도 시스템에 .NET Framework 4.6이 설치되어 있으면 해당 버전이 적용된 것으로 판단해도 됩니다.

 

참고로 .NET Framework중 Client Profile이라고 표현된 것은 일반 데스크탑용 BCL만을 담아둔 버전으로 설치 용량이 다른 버전에 비해서 작다는 특징이 있습니다.

 

IL은 중간언어로서 C#언어 뿐만이 아니라 다양한 언어(C++ 이나 PHP와 같은)에서 생성될 수 있는데 이는 IL코드만의 문법적인 규칙이 존재하고 그 규칙만 지켜진다면 IL을 컴파일하여 닷넷 프레임워크하에서 실행되는 호환언어를 만들 수 있기 때문입니다. 실제로 IL을 컴파일할 수 있는 ILASM.EXE가 존재하는데 이 프로그램은 IL을 실행파일로 컴파일해주는 역활을 수행합니다.

 

그 밖에 닷넷 기반 프로그램을 만드는 언어를 말할때는 꼭 2가지를 꼭 언급해야 합니다. CTS(Common Type System)와 CLS(Common Language System)가 바로 그것인데요. CTS는 공용 타입 시스템으로서 언어가 가져야할 규칙(상속이나 멤버 접근범위등)같은걸 정의하고 있으며 닷넷 언어는 CTS를 만족하는 범위 내에서 만들어져야 합니다. 하지만 그렇다고 모든 규격을 따를 필요는 없는데 반면 CLS는 공용 언어 사양으로서 언어가 지켜야할 최소한의 규격을 정의하고 있습니다. 최소한이라는 말에 주목해 주세요. 적어도 언어는 CLS의 모든 부분을 만족해야 한다는 것을 의미합니다. 다시 말해 닷넷 호환언어는 CLS 규격을 만족하도록 만들어져야 하고 뒤이어 언어의 확장 범위에 따라 CTS를 부분적으로 만족하도록 하면 되는 것입니다.

 

그렇다면 언어가 아닌 프로그램을 실행하는 구현체도 만들어질 수 있을까? 정답은 '가능하다.'입니다. 마이크로소프트는 이를 위해 CLI(Common Language Infrastructure)라고 하는 규약을 발표합니다. CLI는 이미 언급한 CTS나 IL을 포함해 컴파일결과물에 대한 사양까지, 거의 모든 부분을 포함하는 사양서에 해당합니다. 실제 마이크로소프트는 CLR을 이 CLI규격을 따라 만들었고 리눅스운영체제에서 닷넷 프로그램을 실행할 수 있도록 해주는 Mono프로젝트도 CLI규격에 따라 만들어진 결과물입니다.

BCL, CLI, CLR, CLS, cts, il
0 0
Programming/.NET

 1. CreateUserWizard를 이용한 사용자 추가

 

ASP.NET에서는 회원가입을 통한 간단한 사용자 추가기능을 제공합니다. 사용자가 존재해야 해당 사용자의 정보를 확인하고 인증처리를 수행할 수 있을 것입니다.

 

웹페이지에 다음과 같이 CreateUserWizard 컨트롤을 끌어다 놓습니다.

 

<asp:CreateUserWizard ID="CreateUserWizard1" runat="server">
    <WizardSteps>
        <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">
        </asp:CreateUserWizardStep>
        <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
        </asp:CompleteWizardStep>
    </WizardSteps>
</asp:CreateUserWizard>

 

이 컨트롤을 끌어다 놓는 것으로 사용자 추가(회원가입)를 위한 기능이 마련된 것입니다. CreateUserWizardStep 템플릿은 회원가입을 받는 입력폼 템플릿이며 CompleteWizardStep는 회원가입 후 축하메세지등을 보이기 위한 템플릿입니다.

 

 

페이지를 실행시킨뒤 입력칸을 채우고 'Create User'버튼을 누릅니다.

 

 

사용자가 성공적으로 생성되었습니다. 만약 membership에 관한 공급자설정을 따로 변경하지 않았다면 이 사용자의 정보는 내부 App_Data폴더에 있는 mdf파일에 저장될 것입니다.(mdf가 없으면 자동생성하지만 App_Data폴더에 적절한 권한이 부여되어 있어야 합니다.) 내부 mdf가 아닌 외부 DB서버에 데이터저장을 설정하고자 한다면 web.config를 수정하여 membership 공급자설정을 변경해야 합니다.

 

<membership>
  <providers>
    <clear/>
    <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, 

System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 

connectionStringName="sqlprovider" enablePasswordRetrieval="false" enablePasswordReset="true" 

requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" 

passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" 

minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" 

passwordStrengthRegularExpression=""/>
  </providers>
</membership>

 

위 예제는 기본공급자 설정에서 connectionStringName만 수정해 DB연결정보를 바꾼것입니다. sqlprovider는 web.config에 다음과 비슷한 형태로 연결정보가 정의되어 있다는 것을 가정합니다.

 

<connectionStrings>

   <add name="sqlprovider" connectionString="Data Source=localhost;Initial 

Catalog=aspnetdb;User ID=sa;Password=1234!;"/>
</connectionStrings>

 

그 밖에도 여러가지 설정사항이 있는데 이 설정을 통해서 회원가입에 대한 몇몇 규칙을 설정할 수 있습니다. 이를테면 minRequiredPasswordLength속성은 사용자가 암호를 설정할때 최소 암호길이를 지정하며 enablePasswordRetrieval은 암호 분실시 조회기능사용여부를 enablePasswordReset은 암호재설정 지원여부를 requiresQuestionAndAnswer은 보안용 질문과 대답을 받을지 여부를 requiresUniqueEmail은 이메일정보를 받을지의 여부를 PasswordFormat은 암호가 저장될때 암호화방식을 지정하는 속성입니다.

 

회원가입을 완료한 뒤 DB에 실제 사용자정보가 추가되어 있는지를 확인해 보겠습니다.

 

Select *
From [dbo].[aspnet_Membership];

 

 

CreateUserWizard 컨트롤은 기본적으로 아이디와 암호, 이메일, 보안질문/답변정보를 사용자에게 요구합니다. 그러나 필요에 따라서는 사용자에게 추가적인 정보를 요구할 수도 있는데 추가적인 정보를 요구하는 방법에 대해 알아보겠습니다.

 

예제에서는 사용자의 나이에 대한 정보를 요구해야 한다고 가정하겠습니다.

 

aspx의 디자인모드로 이동해서 CreateUserWizard 컨롤의 스마트태그를 클릭합니다. 그리고 스마트태그 메뉴에서 Customize Create User Step을 클릭하십시오.

 

위 작업과정을 거치신 후 다시 소스보기를 하시면 기본템플릿에 대한 ContentTemplate HTML코드가 나열된 것이 보일 것입니다.

 

<asp:CreateUserWizard ID="CreateUserWizard1" runat="server">
    <WizardSteps>
        <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">
            <ContentTemplate>
                <table>
                    <tr>
                        <td align="center" colspan="2">Sign Up for Your New Account</td>
                    </tr>
                    <tr>
                        <td align="right">

                            <asp:Label ID="UserNameLabel" runat="server" 

AssociatedControlID="UserName">User Name:</asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="UserName" runat="server"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="UserNameRequired" runat="server" 

ControlToValidate="UserName" ErrorMessage="User Name is required." ToolTip="User Name is required." 

ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="right">
                            <asp:Label ID="PasswordLabel" runat="server" AssociatedControlID="Password">

Password:</asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="PasswordRequired" runat="server" 

ControlToValidate="Password" ErrorMessage="Password is required." ToolTip="Password is required." 

ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="right">
                            <asp:Label ID="ConfirmPasswordLabel" runat="server" 

AssociatedControlID="ConfirmPassword">Confirm Password:</asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="ConfirmPassword" runat="server" TextMode="Password"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="ConfirmPasswordRequired" runat="server" 

ControlToValidate="ConfirmPassword" ErrorMessage="Confirm Password is required." 

ToolTip="Confirm Password is required." ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="right">
                            <asp:Label ID="EmailLabel" runat="server" AssociatedControlID="Email">E-mail:</asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="Email" runat="server"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="EmailRequired" runat="server" ControlToValidate="Email" 

ErrorMessage="E-mail is required." ToolTip="E-mail is required." ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="right">
                            <asp:Label ID="QuestionLabel" runat="server" AssociatedControlID="Question">

Security Question:</asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="Question" runat="server"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="QuestionRequired" runat="server" ControlToValidate="Question" 

ErrorMessage="Security question is required." ToolTip="Security question is required." 

ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="right">
                            <asp:Label ID="AnswerLabel" runat="server" AssociatedControlID="Answer">

Security Answer:</asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="Answer" runat="server"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="AnswerRequired" runat="server" ControlToValidate="Answer" 

ErrorMessage="Security answer is required." ToolTip="Security answer is required." 

ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="center" colspan="2">
                            <asp:CompareValidator ID="PasswordCompare" runat="server" ControlToCompare="Password" 

ControlToValidate="ConfirmPassword" Display="Dynamic" 

ErrorMessage="The Password and Confirmation Password must match." ValidationGroup="CreateUserWizard1">

</asp:CompareValidator>
                        </td>
                    </tr>
                    <tr>
                        <td align="center" colspan="2" style="color:Red;">
                            <asp:Literal ID="ErrorMessage" runat="server" EnableViewState="False"></asp:Literal>
                        </td>
                    </tr>
                </table>
            </ContentTemplate>
        </asp:CreateUserWizardStep>
        <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
        </asp:CompleteWizardStep>
    </WizardSteps>
</asp:CreateUserWizard>.

 

위 내용을 수정하여 E-Mail아래 나이를 입력받을 수 있는 필드를 하나 추가해 보도록 하겠습니다.

 

<tr>
    <td align="right">
        <asp:Label ID="EmailLabel" runat="server" AssociatedControlID="Email">E-mail:</asp:Label>
    </td>
    <td>
        <asp:TextBox ID="Email" runat="server"></asp:TextBox>
        <asp:RequiredFieldValidator ID="EmailRequired" runat="server" ControlToValidate="Email" ErrorMessage="E-mail is required." ToolTip="E-mail is required." ValidationGroup="CreateUserWizard1">*</asp:RequiredFieldValidator>
    </td>
</tr>
<tr>
    <td align="right">
        <asp:Label ID="AgeLabel" runat="server" AssociatedControlID="Age">Age:</asp:Label>
    </td>
    <td>
        <asp:TextBox ID="Age" runat="server"></asp:TextBox>
    </td>
</tr>
.

 

위와 같이 수정 후 페이지를 실행해 보면 추가한 나이 입력칸이 보임을 알 수 있습니다. CreateUserWizard컨트롤로는 여기까지가 가능한 수준이며 이렇게 입력필드를 추가한다고 해도 실제 DB저장방식까지 바뀌지 않습니다. 따라서 별도의 테이블을 만들어 사용자의 나이를 저장할 수 있도록 해야 하는데 이때는 CreatedUser 이벤트를 사용합니다.

 

<asp:CreateUserWizard ID="CreateUserWizard1" runat="server"

OnCreatedUser="CreateUserWizard1_CreatedUser">

 

protected void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
{
    TextBox txt_age = CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("Age") as TextBox;
    Response.Write(txt_age.Text);
}

 

CreateUserWizard 컨트롤의 ContentTemplate안에 생성한 컨트롤은 직접적으로 접근할 수 없기 때문에 ContentTemplate내부에서 필요한 컨트롤을 위와같이 직접 찾아야 합니다. 이런 방법으로 컨트롤의 속성값을 가져와 사용자가 생성될때 필요한 DB처리를 수행하면 됩니다.

 

 2. CreateUser API를 이용한 사용자 추가

 

사용자 추가 부분은 굳이 컨트롤을 이용하지 않고도 코드를 통해 수행할 수 있습니다. Membership 클래스의 CreateUser 메소드를 이용하는 것입니다.

 

Membership.CreateUser("anchor37", "a1234%^");

 

이 메소드도 대략 5가지 정도로 오버로드되어 있는데 최종적으로 앞서 살펴보았던 CreateUserWizard컨트롤에서 입력된 모든 사항을 등록할 수 있습니다.

 

CreateUserWizard컨트롤은 필요한 정보를 입력받을 때 부터 잘못된 입력값을 차단하지만(예를 들어 비밀번호등) API를 이용하면 UI에서 직접 처리하지 않는한 이를 차단할 방법이 없기 때문에 잘못된 정보가 입력되려 하면 대신 예외를 발생시킵니다.

 

try
{
    Membership.CreateUser("anchor37", "123");
}
catch (Exception ex) {
    Response.Write("다음과 같은 이유로 사용자를 생성하지 못했습니다.<br />" + ex.Message);
}

 

좀더 자세한 정보를 사용자에게 제공하려면 StatusCode속성을 활용해도 됩니다.

 

try
{
    Membership.CreateUser("anchor37", "123");
}
catch (MembershipCreateUserException ex) {
    switch (ex.StatusCode) {
        case MembershipCreateStatus.InvalidPassword:
            Response.Write("비밀번호 입력이 잘못되었습니다.");
            break;
        case MembershipCreateStatus.InvalidEmail:
            Response.Write("이메일 형식이 맞지 않습니다.");
            break;
        default:
            Response.Write(ex.Message);
            break;
    }
}.

 

이 외에도 DulicateEmail, DuplicateProviderUserKey, DuplicateUserName, InvaildAnswer, InvaildProviderUserKey, InvaildQuestion, InvaildUserName, ProviderError, Success, UserRejected등의 오류코드를 사용할 수 있습니다.

0 0
Programming/.NET

일반적으로 데이터저장을 위해서는 쿠키나 세션과 같은 대중적인(?) 방법이 있지만 ASP.NET에서는 그 외에도

아래와 같은 데이터 저장방법을 활용할 수 있습니다.

 

 1. HttpContext.Current.Items

 

HttpContext.Current.Items를 활용하면 세션이나 쿠키, 뷰상태를 대신해 임시적으로 데이터를 저장할 수 있습니다.

 

HttpContext.Current.Items["aaa"] = "cliel";

Response.Write(HttpContext.Current.Items["aaa"].ToString());

 

HttpContext.Current.Items도 세션처럼 단순문자열부터 개체까지 다양한 Type의 데이터를 저장하고 가져올 수 있습니다.

 

 2. Application

 

Application 개체를 활용하면 ASP.NET 애플리케이션에서 전역변수형태로 데이터를 담아둘 수 있습니다. 즉, 애플리케이션 전역에서 공통된 변수로 데이터를 동일하게 관리하려면 Application개체를 사용하시면 됩니다.

 

Application.Lock();
Application["tmpdata"] = "cliel";
Application.UnLock();

 

Application개체는 다중 스레드에서의 요청에서 접근될 수 있으므로 값을 저장하거나 변경하는 경우에는 Lock 메소드를 호출하여 잠금을 유지하고 값설정이 끝나면 UnLock 메소드로 잠금을 종료하시기 바랍니다.

 

다만 UnLock메소드는 Lock메소드를 호출한 HttpRequest 개체수명이 종료되면 자동으로 잠금이 해제됩니다.

 

마지막으로 뷰스테이트와 숨김필드를 통해서도 값을 저장하는 방법이 있으나 단순한 내용이 아닌 비교적 보안이 요구되는 데이터인 경우에는 권장하지 않습니다.

0 0
1
블로그 이미지

클리엘