천객만래 [千客萬來] (It has an interminable succession of visitors)

[asp.net] The compiler failed with error code 128



Server Error in '/net' Application.
--------------------------------------------------------------------------------

Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately. 

Compiler Error Message: The compiler failed with error code 128.

Show Detailed Compiler Output:

C:\WINNT\system32> "C:\WINNT\Microsoft.NET\Framework\v2.0.50727\csc.exe" /t:library /utf8output /R:"C:\WINNT\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll" /R:"C:\WINNT\assembly\GAC_MSIL\System.Web.Services\2.0.0.0__b03f5f7f11d50a3a\System.Web.Services.dll" /R:"C:\WINNT\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll" /R:"C:\WINNT\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\net\25abbb5f\59ac6f1f\assembly\dl3\d9f2321a\8c67f30e_f8a3c601\App_Web_sl75aj7z.DLL" /R:"C:\WINNT\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll" /R:"C:\WINNT\assembly\GAC_MSIL\System.Configuration\2.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll" /R:"C:\WINNT\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll" /R:"C:\WINNT\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll" /R:"C:\WINNT\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll" /R:"C:\WINNT\assembly\GAC_MSIL\System.Web.Mobile\2.0.0.0__b03f5f7f11d50a3a\System.Web.Mobile.dll" /R:"C:\WINNT\assembly\GAC_32\System.EnterpriseServices\2.0.0.0__b03f5f7f11d50a3a\System.EnterpriseServices.dll" /out:"C:\WINNT\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\net\25abbb5f\59ac6f1f\App_Web_default.aspx.cdcab7d2.d9t21czt.dll" /D:DEBUG /debug+ /optimize- /w:4 /nowarn:1659;1699  "C:\WINNT\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\net\25abbb5f\59ac6f1f\App_Web_default.aspx.cdcab7d2.d9t21czt.0.cs" "C:\WINNT\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\net\25abbb5f\59ac6f1f\App_Web_default.aspx.cdcab7d2.d9t21czt.1.cs"



-------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:2.0.50727.42; ASP.NET Version:2.0.50727.42 

CAUSE
This problem occurs when a third-party component is running in the W3wp.exe process that has attached a console to the process. The Vbc.exe compiler process and the Csc.exe compiler process then inherit this console. If the console uses a windowstation that does not contain a desktop named DEFAULT, this may cause the compiler processes, or any other process that is spawned from the W3wp.exe process that depends on the User32.dll file, not to start. 

RESOLUTION
To resolve this problem, apply the hotfix in the following Microsoft Knowledge Base article:
839229  Multiple Web-based programs or Component Object Model-based programs stop responding on a computer that is running Windows Server 2003 or Windows 2000 
Note This hotfix is also included in Microsoft Windows Server 2003 Service Pack 1 (SP1).

CAUSE
This problem occurs because the worker process that tries to start the Microsoft ASP.NET compiler process is running under the Network Service identity or under an account that is not in the Administrators group. When the ASP.NET compiler process tries to start Vbc.exe or Csc.exe, the process initialization routine fails. This failure occurs because a dependent DLL fails during its initialization routine. If any of the DLLs for a process return a failure in their initialization routine, the operating system stops the process startup and returns an error code of 128 (ERROR_WAIT_NO_CHILDREN).

RESOLUTION
To resolve this problem, use either of the following methods. Use the method that fits your situation the best. 

Method 1: Prevent the World Wide Web Publishing Service from interacting with the desktop
You can prevent the World Wide Web Publishing Service from interacting with the desktop. This is the preferred method. 

Note By default, the World Wide Web Publishing Service is not configured to interact with the desktop. 

To do this, follow these steps: 
1. Click Start, click Run, type cmd, and then click OK. 
2. At the command prompt, type control admintools, and then press ENTER. 
3. Double-click Services. 
4. In the Services pane, locate World Wide Web Publishing Service. 
5. Right-click World Wide Web Publishing Service, and then click Properties. 
6. Click the Log On tab. 
7. Click to clear the Allow service to interact with desktop check box. 
8. Click OK. 

Method 2: Change the application pool identity to the Local System identity
Warning This workaround may make your computer or your network more vulnerable to attack by malicious users or by malicious software such as viruses. We do not recommend this workaround but are providing this information so that you can implement this workaround at your own discretion. Use this workaround at your own risk.

You can change the application pool identity to the Local System identity. 

Important You should only use this method if the following conditions are true: 
• The ASP.NET Web application must interact with the desktop. 
• You understand the security implications of changing the application pool identity to the Local System identity. For more information about the Local System account and the Network Service account, see the "Applications as NT Services" section at the following Microsoft Developer Network (MSDN) Web site: 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnentsrv/html/netenterpriseandcomplus.asp
To do this, follow these steps: 
1. Click Start, click Run, type cmd, and then click OK. 
2. At the command prompt, type control admintools, and then press ENTER. 
3. Double-click Internet Information Services (IIS) Manager. 
4. Expand the computer name, expand Application Pools, right-click the name of the application pool that you want to modify, and then click Properties. 
5. Click the Identity tab. 
6. Click Predefined, and then click Local System. 
7. Click OK. 

Method3:ran the the aspnet_regiis.exe and it solved my problem

C:\WINNT\Microsoft.NET\Framework\version\aspnet_regiis.exe -i 


MORE INFORMATION
ASP.NET does not try to compile a page after an error is encountered during the compilation process. The error is cached until the process is recycled, or until the page or one of its dependencies is modified. When the worker process is tied to the console windowstation (Winsta0), the World Wide Web Publishing Service adds the security identifier (SID) for the IIS_WPG group to the access control list (ACL) for the Winsta0 object. Then, the World Wide Web Publishing Service starts the W3wp.exe process.

When a user logs on to or off a console session, the Winlogon process rebuilds the ACL for the Winsta0 object and removes the IIS_WPG SID from the ACL. Any child processes that are started by the worker process (W3wp.exe) may not start. These processes include the Csc.exe and Vbc.exe processes. 

When a service is not configured to interact with the desktop, the process uses a non-interactive windowstation that is unaffected by a user logging on to the console.

Note You can log on to the console in Windows Server 2003 by any one of the following methods: 
• Log on to the computer interactively. 
• Use the Remote Desktop client application. For example, type mstsc.exe /console at a command prompt.  
• Use a third-party application that accesses the console session.


Posted by SB패밀리

[개발/VC++] 리소스 DLL 만들기 



리소스 전용 DLL은 아이콘, 비트맵, 문자열 및 대화 상자 등의 리소스만 들어 있는 DLL입니다. 리소스 전용 DLL을 사용하면 여러 프로그램 간에 동일한 리소스 집합을 쉽게 공유할 수 있습니다. 여러 언어로 지역화된 리소스가 있는 응용 프로그램을 제공하는 것도 좋은 방법입니다(MFC 응용 프로그램의 지역화된 리소스: 위성 DLL 참조).

리소스 전용 DLL을 만들려면 새로운 Win32 DLL(비 MFC) 프로젝트를 만든 다음 이 프로젝트에 리소스를 추가합니다.

  • 새 프로젝트 대화 상자에서 Win32 프로젝트를 선택한 다음 Win32 프로젝트 마법사에서 DLL 프로젝트 형식을 지정합니다.

  • 해당 DLL에 사용할 문자열 또는 메뉴와 같은 리소스가 포함된 새 리소스 스크립트를 만든 다음 .RC 파일로 저장합니다.

  • 프로젝트 메뉴에서 기존 항목 추가를 선택한 다음 새로 만든 .rc 파일을 프로젝트에 삽입합니다.

  • /NOENTRY 링커 옵션을 지정합니다. /NOENTRY 옵션은 링커가 _main에 대한 참조를 DLL에 링크하지 않도록 하며, 리소스 전용 DLL을 만들 때 반드시 사용해야 합니다.

  • DLL을 빌드합니다.

리소스 전용 DLL을 사용하는 응용 프로그램은 LoadLibrary를 호출하여 명시적으로 DLL에 링크해야 합니다. 리소스에 액세스하려면 모든 종류의 리소스에 대해 작동하는 일반 함수인 FindResource 및 LoadResource를 호출하거나 다음과 같은 특정 리소스 관련 함수 중 하나를 호출해야 합니다.

  • FormatMessage

  • LoadAccelerators

  • LoadBitmap

  • LoadCursor

  • LoadIcon

  • LoadMenu

  • LoadString

응용 프로그램이 리소스 사용을 마친 경우에는 FreeLibrary를 호출해야 합니다.

개념


============================================
※ 빌드하기 전에 프로젝트->속성->링커->명령줄 메뉴에서 추가옵션란에 "/NOENTRY" 옵션을 추가해준다.
릴리즈 모드로 컴파일해서 *.dll 생성된 파일을 확인후 리소스 DLL 사용

사용예:

HMODULE hRes = NULL;
hRes = LoadLibrary("cs.dll");
// hRes NULL이 아니면 성공
LoadString(hRes, IDS_STRING101, str, 256);
hMenu=LoadMenu(hRes, "IDR_MENU1");

Posted by SB패밀리

 

[개발/VC++] 리소스 DLL 만들기

 

리소스 전용 DLL은 아이콘, 비트맵, 문자열 및 대화 상자 등의 리소스만 들어 있는 DLL입니다. 리소스 전용 DLL을 사용하면 여러 프로그램 간에 동일한 리소스 집합을 쉽게 공유할 수 있습니다. 여러 언어로 지역화된 리소스가 있는 응용 프로그램을 제공하는 것도 좋은 방법입니다(MFC 응용 프로그램의 지역화된 리소스: 위성 DLL 참조).

리소스 전용 DLL을 만들려면 새로운 Win32 DLL(비 MFC) 프로젝트를 만든 다음 이 프로젝트에 리소스를 추가합니다.

  • 새 프로젝트 대화 상자에서 Win32 프로젝트를 선택한 다음 Win32 프로젝트 마법사에서 DLL 프로젝트 형식을 지정합니다.

  • 해당 DLL에 사용할 문자열 또는 메뉴와 같은 리소스가 포함된 새 리소스 스크립트를 만든 다음 .RC 파일로 저장합니다.

  • 프로젝트 메뉴에서 기존 항목 추가를 선택한 다음 새로 만든 .rc 파일을 프로젝트에 삽입합니다.

  • /NOENTRY 링커 옵션을 지정합니다. /NOENTRY 옵션은 링커가 _main에 대한 참조를 DLL에 링크하지 않도록 하며, 리소스 전용 DLL을 만들 때 반드시 사용해야 합니다.

  • DLL을 빌드합니다.

리소스 전용 DLL을 사용하는 응용 프로그램은 LoadLibrary를 호출하여 명시적으로 DLL에 링크해야 합니다. 리소스에 액세스하려면 모든 종류의 리소스에 대해 작동하는 일반 함수인 FindResourceLoadResource를 호출하거나 다음과 같은 특정 리소스 관련 함수 중 하나를 호출해야 합니다.

  • FormatMessage

  • LoadAccelerators

  • LoadBitmap

  • LoadCursor

  • LoadIcon

  • LoadMenu

  • LoadString

응용 프로그램이 리소스 사용을 마친 경우에는 FreeLibrary를 호출해야 합니다.

개념

DLL


============================================
※ 빌드하기 전에 프로젝트->속성->링커->명령줄 메뉴에서 추가옵션란에 "/NOENTRY" 옵션을 추가해준다.
릴리즈 모드로 컴파일해서 *.dll 생성된 파일을 확인후 리소스 DLL 사용

사용예:

HMODULE hRes = NULL;
hRes = LoadLibrary("cs.dll");
// hRes NULL이 아니면 성공
LoadString(hRes, IDS_STRING101, str, 256);
hMenu=LoadMenu(hRes, "IDR_MENU1");
Posted by SB패밀리

MSDN에서 찾았습니다.

Example C Program: Verifying the Signature of a PE File
http://msdn2.microsoft.com/en-us/library/aa382384(VS.85).aspx

//-------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Example of verifying the embedded signature of a PE file by using 
// the WinVerifyTrust function.

#define _UNICODE 1
#define UNICODE 1

#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>

// Link with the Wintrust.lib file.
#pragma comment (lib, "wintrust")

BOOL VerifyEmbeddedSignature(LPCWSTR pwszSourceFile)
{
    LONG lStatus;
    DWORD dwLastError;

    // Initialize the WINTRUST_FILE_INFO structure.

    WINTRUST_FILE_INFO FileData;
    memset(&FileData, 0, sizeof(FileData));
    FileData.cbStruct = sizeof(WINTRUST_FILE_INFO);
    FileData.pcwszFilePath = pwszSourceFile;
    FileData.hFile = NULL;
    FileData.pgKnownSubject = NULL;

    /*
    WVTPolicyGUID specifies the policy to apply on the file
    WINTRUST_ACTION_GENERIC_VERIFY_V2 policy checks:
    
    1) The certificate used to sign the file chains up to a root 
    certificate located in the trusted root certificate store. This 
    implies that the identity of the publisher has been verified by 
    a certification authority.
    
    2) In cases where user interface is displayed (which this example
    does not do), WinVerifyTrust will check for whether the  
    end entity certificate is stored in the trusted publisher store,  
    implying that the user trusts content from this publisher.
    
    3) The end entity certificate has sufficient permission to sign 
    code, as indicated by the presence of a code signing EKU or no 
    EKU.
    */

    GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    WINTRUST_DATA WinTrustData;

    // Initialize the WinVerifyTrust input data structure.

    // Default all fields to 0.
    memset(&WinTrustData, 0, sizeof(WinTrustData));

    WinTrustData.cbStruct = sizeof(WinTrustData);
    
    // Use default code signing EKU.
    WinTrustData.pPolicyCallbackData = NULL;

    // No data to pass to SIP.
    WinTrustData.pSIPClientData = NULL;

    // Disable WVT UI.
    WinTrustData.dwUIChoice = WTD_UI_NONE;

    // No revocation checking.
    WinTrustData.fdwRevocationChecks = WTD_REVOKE_NONE; 

    // Verify an embedded signature on a file.
    WinTrustData.dwUnionChoice = WTD_CHOICE_FILE;

    // Default verification.
    WinTrustData.dwStateAction = 0;

    // Not applicable for default verification of embedded signature.
    WinTrustData.hWVTStateData = NULL;

    // Not used.
    WinTrustData.pwszURLReference = NULL;

    // Default.
    WinTrustData.dwProvFlags = WTD_SAFER_FLAG;

    // This is not applicable if there is no UI because it changes 
    // the UI to accommodate running applications instead of 
    // installing applications.
    WinTrustData.dwUIContext = 0;

    // Set pFile.
    WinTrustData.pFile = &FileData;

    // WinVerifyTrust verifies signatures as specified by the GUID 
    // and Wintrust_Data.
    lStatus = WinVerifyTrust(
        NULL,
        &WVTPolicyGUID,
        &WinTrustData);

    switch (lStatus) 
    {
        case ERROR_SUCCESS:
            /*
            Signed file:
                - Hash that represents the subject is trusted.

                - Trusted publisher without any verification errors.

                - UI was disabled in dwUIChoice. No publisher or 
                    time stamp chain errors.

                - UI was enabled in dwUIChoice and the user clicked 
                    "Yes" when asked to install and run the signed 
                    subject.
            */
            wprintf_s(L"The file \"%s\" is signed and the signature "
                L"was verified.\n",
                pwszSourceFile);
            break;
        
        case TRUST_E_NOSIGNATURE:
            // The file was not signed or had a signature 
            // that was not valid.

            // Get the reason for no signature.
            dwLastError = GetLastError();
            if (TRUST_E_NOSIGNATURE == dwLastError ||
                    TRUST_E_SUBJECT_FORM_UNKNOWN == dwLastError ||
                    TRUST_E_PROVIDER_UNKNOWN == dwLastError) 
            {
                // The file was not signed.
                wprintf_s(L"The file \"%s\" is not signed.\n",
                    pwszSourceFile);
            } 
            else 
            {
                // The signature was not valid or there was an error 
                // opening the file.
                wprintf_s(L"An unknown error occurred trying to "
                    L"verify the signature of the \"%s\" file.\n",
                    pwszSourceFile);
            }

            break;

        case TRUST_E_EXPLICIT_DISTRUST:
            // The hash that represents the subject or the publisher 
            // is not allowed by the admin or user.
            wprintf_s(L"The signature is present, but specifically "
                L"disallowed.\n");
            break;

        case TRUST_E_SUBJECT_NOT_TRUSTED:
            // The user clicked "No" when asked to install and run.
            wprintf_s(L"The signature is present, but not "
                L"trusted.\n");
            break;

        case CRYPT_E_SECURITY_SETTINGS:
            /*
            The hash that represents the subject or the publisher 
            was not explicitly trusted by the admin and the 
            admin policy has disabled user trust. No signature, 
            publisher or time stamp errors.
            */
            wprintf_s(L"CRYPT_E_SECURITY_SETTINGS - The hash "
                L"representing the subject or the publisher wasn't "
                L"explicitly trusted by the admin and admin policy "
                L"has disabled user trust. No signature, publisher "
                L"or timestamp errors.\n");
            break;

        default:
            // The UI was disabled in dwUIChoice or the admin policy 
            // has disabled user trust. lStatus contains the 
            // publisher or time stamp chain error.
            wprintf_s(L"Error is: 0x%x.\n",
                lStatus);
            break;
    }

    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
    if(argc > 1)
    {
        VerifyEmbeddedSignature(argv[1]);
    }

    return 0;
}

파일이 사인되어 있다면 WinVerifyTrust결과로 ERROR_SUCCESS가 리턴 됩니다.
Posted by SB패밀리

외부응용프로그램 실행하기 (Process.Start 메서드)


윈도우를 종료
System.Diagnostics.Process.Start("cmd.exe","ShutDown.exe -s -f -t 00");


윈도우를 재부팅

System.Diagnostics.Process.Start("cmd.exe","ShutDown.exe -r -f -t 00");

 

특정 폴더 열기
System.Diagnostics.Process.Start("explorer.exe", "C:\\Temp");

특정 사이트 열기
System.Diagnostics.Process.Start("explorer.exe", "http://www.naver.com");

 

도스명령어 실행

System.Diagnostics.Process.Start("cmd.exe","/c dir");

// cmd 옵션에 대해 더 알고싶으면.. c:\>help cmd

 

Process.Start 메서드 사용형식
 

using System.Diagnostics;

//System.Diagnostics 네임스페이스는 시스템 프로세스, 이벤트 로그 및 성능 카운터와 상호 작용할 수 있는 클래스를 제공합니다.

 

public bool Start();
//이 Process 구성 요소의 StartInfo 속성으로 지정된 프로세스 리소스를 시작하거나 다시 사용하여 구성 요소에 연결합니다.

 

Process myProcess = new Process();
string myDocumentsPath =   Environment.GetFolderPath(Environment.SpecialFolder.Personal);
myProcess.StartInfo.FileName = myDocumentsPath + "
\\MyFile.doc";
myProcess.StartInfo.Verb = "Print";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();

 

public static Process Start( ProcessStartInfo startInfo);
// ProcessStartInfo : 파일 이름 및 모든 명령줄 인수를 포함하여 프로세스를 시작하는 데 사용되는 정보
// 시작할 프로세스의 파일 이름 같은 프로세스 시작 정보가 포함된 매개 변수에 의해 지정된
// 프로세스 리소스를 시작하고 해당 리소스를 새 Process 구성 요소에 연결합니다

 

ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
startInfo.Arguments = "
www.naver.com";
Process.Start(startInfo);

 

public static Process Start(string fileName);
// fileName : 프로세스에서 실행될 응용 프로그램 파일 이름입니다.

//문서 또는 응용 프로그램 파일 이름을 지정하여 프로세스 리소스를 시작하고 해당 리소스를 새 Process 구성 요소에 연결합니다

 

Process.Start("IExplore.exe");

 

public static Process Start(string fileName, string arguments);
// arguments : 프로세스를 시작할 때 전달할 명령줄 인수입니다

//응용 프로그램 이름 및 명령줄 인수 집합을 지정하여 프로세스 리소스를 시작하고 해당 리소스를 새 Process 구성 요소에 연결합니다.

 

Process.Start("IExplore.exe", "C:\\myPath\\myFile.htm");
Process.Start("IExplore.exe", "C:\\myPath\\myFile.asp");

 

 

Process 클래스

Process 구성 요소는 컴퓨터에서 실행 중인 프로세스에 대한 액세스를 제공합니다. 간단히 말해 프로세스란 실행 중인 응용 프로그램을 말합니다.

 

Process 구성 요소는 응용 프로그램의 시작, 중지, 제어 및 모니터링을 위한 유용한 도구입니다.
Process 구성 요소를 사용하면 실행 중인 프로세스의 목록을 얻거나 새로운 프로세스를 시작할 수 있습니다. 또한 Process 구성 요소를 사용하여 시스템 프로세스에도 액세스할 수 있습니다.
Process 구성 요소를 초기화한 후에는 해당 구성 요소를 사용하여 실행 중인 프로세스에 대한 정보를 얻을 수 있으며 그러한 정보에는 스레드 집합, 로드된 모듈(.dll 및 .exe 파일), 프로세스가 사용하고 있는 메모리 양과 같은 성능 정보 등이 포함됩니다.

 

프로세스 구성 요소는 속성 그룹에 대한 정보를 한 번에 가져옵니다. Process 구성 요소가 특정 그룹의 한 멤버에 대한 정보를 가져올 때 해당 그룹의 나머지 속성 값이 캐싱되므로 Refresh 메서드를 호출하지 않는 한 그룹의 다른 멤버에 대한 새로운 정보를 가져오지 않습니다. 따라서 속성 값이 Refresh 메서드를 마지막으로 호출하여 얻은 속성 값과 같을 수 있습니다. 이러한 그룹 명세는 운영 체제에 따라 다릅니다.

 

더 자세한 사항은 Microsoft Visual Studio .NET 2003 도움말에서 Process 클래스를 참고하세요.

출처 : http://blog.naver.com/tear230/100002921976

쌈꼬쪼려 소백촌닭
Posted by SB패밀리

Written by 안재우(Jaewoo Ahn), 닷넷엑스퍼트(.netXpert)

 

예전 Voice of .NETXPERT 2003 행사 때, 저희 회사의 김유철 책임이 발표했던 내용인 '아무도 가르쳐 주지 않는 .NET 애플리케이션 개발 Tips 18가지'를 정리해서 올립니다.

 먼저 첫번째로 Visual Studio .NET 관련 팁으로 시작합니다.

 Tip 1. 참조 추가 대화상자에 나의 어셈블리나 컴포넌트를 보이게 할 수 있는 방법은?

 다음과 같이 나타나게 하는 것을 의미합니다.

 

사실 처음에는 GAC(Global Assembly Cache)에 올라가면 나오지 않을까 생각했었는데, 막상 그렇지 않다는 것을 알게 되었습니다.

어쨌든 참조 대화상자에 표시하는 방법은 크게 2가지 방법이 있습니다.

 첫번째 방법은 폴더를 이용하는 방법으로서, 다음 폴더에 위치하면 됩니다.

C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\PublicAssemblies

 두번째 방법은 레지스트리를 이용하는 방법으로서, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.0\AssemblyFolders의 PublicAssemblies 하위키를 이용하면 됩니다.

 Tip 2. 디버깅 빌드 속도 개선?

기본적으로 VS.NET은 빌드 수행 시 솔루션 내의 모든 프로젝트에 대해 빌드를 수행합니다. 그런데, 프로젝트의 갯수가 많아지다보면 디버깅을 위해 빌드를 수행하는데 걸리는 시간이 점점 많이 걸리게 됩니다.

이를 개선하기 위해서는 다음과 같이 옵션을 지정하여, 빌드 속도를 개선할 수 있습니다.

 

Tip 3. 인텔리센스 동적 도움말

내가 직접 만든 컴포넌트에 다음과 같이 인텔리센스 도움말이 나타나게 하려면?

 

일단 기본적으로 다음과 같은 XML 주석을 달면 된다는 것은 대부분 알고 있을 것입니다.

 

그런데 이 컴포넌트를 담고 있는 프로젝트와 이 컴포넌트를 사용하는 프로젝트가 동일 솔루션 내에 있는 경우에는 자동적으로 인텔리센스 도움말이 표시되지만, 문제는 이 컴포넌트의 DLL만 배포해서, DLL 참조를 수행할 경우입니다.

이러한 경우, 다음과 같이 프로젝트 속성에서 구성속성/빌드/XML 문서 파일에서 XML 파일의 이름을 지정해서 빌드를 수행할 때 XML 파일을 생성하게 합니다.

이제 DLL과 XML을 같이 배포하고, 이 DLL을 다른 프로젝트에서 참조하면 자동적으로 해당 XML 파일이 로드되어 인텔리센스 도움말을 지원하게 됩니다.

 

나머지 팁은 나중에 이어서 올리도록 하겠습니다.

 두번째로 .NET Framework 관련 팁 2가지입니다.

 

Tip 4. StringBuilder의 잘못된 사용

문자열(System.String)의 경우, 소위 말하는 Immutable 패턴을 따르고 있기 때문에 독특한 특성을 가지는데, 간략하게 설명하면 다음과 같습니다. 우선 다음 코드를 봅시다.

 string s = "Hello";
s = s + ", ";
s = s + "World !";

이렇게 했을 때, 실제 메모리 상에서는 다음과 같이 문자열에 해당하는 메모리 공간이 3번 점유됩니다.

 

Memory #0 : [Hello]
Memory #1 : [Hello, ]
Memory #2 : [Hello, World!]

즉 동일한 메모리 주소(#0)에 추가되는 것이 아니라, 문자열 연산을 수행한 결과의 문자열이 새로운 메모리 주소에 할당된다는 것입니다.

이에 비해 StringBuilder라는 클래스를 우리가 일반적으로 생각하는 방법처럼 동일 메모리 주소(#0)에 문자열을 추가함으로써 재할당의 오버헤드, 불필요한 메모리 공간의 낭비를 줄입니다. StringBuilder를 사용할 경우, 위 코드는 다음과 같이 됩니다.

StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.Append(", ");
sb.Append("World!");
 

이 때문에 상당수의 .NET 프로그래밍 팁에서는 문자열 연산을 여러번 수행하는 경우, StringBuilder를 사용하면 성능이 좋아진다고 설명해놓은 경우가 많습니다.

문제는 이를 맹신하고 남용하는 사람들이 많다는 것입니다. 실제로 다른 .NET 팁을 보고 나서 무조건 StringBuilder를 사용해서 코드를 작성하는 프로그래머를 본 적도 있습니다. 과연 그게 맞을까요? 위에서 본 두 가지 경우와 아래의 경우 중 성능이 가장 좋은 것은 어느 것일까요?

string s = "Hello" + ", " + "World!";

가장 바람직한 것은 바로 위, 즉 맨 마지막의 경우입니다. 바로 위처럼 작성한 경우, C# 컴파일러는 MSIL을 생성할 때 위 코드를 다음과 같이 최적화합니다.

string s = "Hello, World!";

그러므로 문자열 상수 연산이나 보통 5개 이하의 문자열 연산에는 StringBuilder를 사용하지 말고 + 연산자를 사용하는 것이 바람직합니다. StringBuilder를 써야 할 경우는 다수의 문자열 연산을 수행하거나, 반복문(for, while 루프 등) 내에서 문자열 연산을 수행할 경우입니다.

성능 뿐만 아니라 코드 가독성 측면에서도 + 연산을 사용하는 것이 StringBuilder.Append()를 사용하는 것보다 훨씬 나으므로, 코드 생성기나 HTML 렌더링과 같은 특수한 작업을 수행하지 않는 일반적인 경우라면 + 연산을 사용하는 것을 권장하고 싶습니다.

 

Tip 5. 콘솔 애플리케이션 출력 캡쳐

요즘은 대부분 웹이나 윈도우 애플리케이션으로 작성하긴 하지만, 간혹 가다 콘솔(커맨드라인) 애플리케이션을 작성해야 할 경우도 있습니다. 이럴 경우, 콘솔 애플리케이션의 출력 내용을 캡쳐하는 방법은 없을까요?

System.Diagnostics.ProcessStartInfo 클래스를 사용하면 이를 간단하게 수행할 수 있습니다. 다음 코드를 보죠.

Process cmd = new Process();
ProcessStartInfo procInfo = new procInfo();
procInfo.RedirectStandardOutput = true;  // 표준 출력을 Redirect
procInfo.UseShellExecute = false;             // 표준 입/출력 Redirect 시에는 false로 설정
procInfo.CreateNoWindow = true;            // 윈도우를 생성하지 않음
cmd.StartInfo = procInfo;
cmd.Start();
cmd.StandardOutput.Read();                    // StreamReader를 통해 출력을 얻음
 
다음 번엔 ADO.NET 관련 팁을 올리도록 하겠습니다.
 

세번째로 ADO.NET 관련 팁 2가지입니다.

 

Tip 6. Strict Typed Parameter 사용하기

데이터액세스 작업을 수행하다보면 Parameterized Query나 SP를 사용하는 경우, SqlParameter와 같은 Parameter Class를 사용하여 매개변수를 전송해야 하는 경우가 많습니다.

SqlParameter를 만드는 방법에는 여러가지가 있지만, 일반적으로 다음 생성자를 사용하는 경우가 많습니다.

SqlParameter(string parameterName, object value);

매개변수명/값 쌍으로만 전달하면 되므로, 다음과 같이 작성하기만 하면 되어서 전반적으로 매우 편리하기 때문입니다.

 SqlParameter param = new SqlParameter("@ProductID", "50");  // Case1
SqlParameter param = new SqlParameter("@ProductID", 50);    // Case2

둘 중 어느 것을 사용하더라도 상관은 없습니다. 그런데, 이 두개만 하더라도 실제 SQL 프로파일러로 찍어보면 결과가 서로 다른 것을 알 수 있습니다.

exec ...@ProductID', N'@ProductID nvarchar(4000)', @ProductID = N'50'  // Case1
exec ...@ProdcutID', N'@ProductID int', @ProductID = 50                         // Case2

이러한 결과가 나타나는 이유는 ADO.NET이 매개변수의 값으로부터 매개변수의 Type을 추론해내기 때문입니다. 명시적으로 지정을 하든, 지정을 하지 않든 Type은 어느 경우나 반드시 필요합니다. 위의 예에서는 Case2가 좀 낫긴 하지만, 만약 ProductID가 int가 아닌 다른 숫자형이었다면?

 결론적으로 가장 바람직한 것은 매개변수의 Type을 정확하게 지정해주는 것이 좋다는 것입니다. 그러므로 매개변수명/값 쌍 형태보다는 다음과 같이 매개변수명/Type 쌍으로 생성한 후, 값을 따로 지정해주는 것이 보다 바람직합니다. Type 외에도 Size, Precision 등이 있는 경우, 이를 지정해주면 더욱 더 좋습니다.

SqlParameter param = new SqlParameter("@ProductID", SqlDbType.Int);
param.Value = 50;

프로파일링을 해보면 알 수 있지만 보다 많은 사항을 명시적으로 지정해줄 수록 서버 측 리소스를 절감해서 쿼리 속도 및 성능이 개선되는 것을 알 수 있습니다. 정리하자면 개발자의 편의성과 성능은 반비례한다는 것이 되겠죠. ^^

 

Tip 7. ADO Recordset으로부터 DataSet 만들기

기존 시스템이 ASP로 되어 있었고, ADO Recordset을 반환하는 비즈니스 로직 컴포넌트를 호출해서 데이터를 가져오는 3-Tier 구조로 되어 있었다고 가정합시다.

그러던 어느날 프리젠테이션 부분을 ASP.NET으로 교체한다고 회사에서 결정을 내렸습니다. 윗 사람은 이미 비즈니스 로직은 다 작성되어 있어서 프리젠테이션 부분만 작성하면 되니깐 금방 하겠다고 하면서 프로젝트 일정을 말도 안되게 짧게 잡아 놓았습니다. 안된다고 말을 할려니, 이전에 3-Tier 구조의 장점을 역설했던 일이 거짓말이 되어버리게 생겼습니다. 그렇다고 기존 Recordset을 그대로 사용하려니 ASP.NET에서의 데이터바인딩과 같은 장점을 전혀 사용할 수가 없습니다. 어쩔수 없이 밤을 새서라도 기존 비즈니스 로직 컴포넌트를 .NET으로 재작성하려고 생각을 했는데, 엎칱데 덮친 격으로 기존 컴포넌트에 해당하는 소스가 현재 나한테 없습니다. 이러한 경우, 도대체 어떻게 해야 할까요?

이러한 경우, 기존의 Legacy 로직을 재사용하는 것으로 방향을 잡는 것이 바람직합니다. 처음부터 재작성을 하기에는 시간과 비용이 모자라기 때문입니다.

이때 유용한 것이 바로 OleDbDataAdapter의 Fill 메서드입니다. 다른 DataAdapter들과는 다르게, OleDbDataAdapter의 Fill 메서드에는 다음과 같은 오버로딩이 존재합니다.

public int Fill(DataSet dataSet, object ADODBRecordSet, string srcTable);

이 메서드는 ADODBRecordSet의 내용을 DataSet 내에 srcTable이라는 이름의 DataTable로 채워 넣는 역할을 수행합니다. 이렇게 하여 RecordSet의 내용을 DataSet으로 옮길 수 있게 됩니다.

이렇게 할 때 장점은 다음과 같습니다.

첫째, 기존 Legacy 로직을 수정/변경 없이 그대로 재사용이 가능합니다.

둘째, 기존 Recordset이 Connection 기반이더라도 이를 통해 Disconnected 모델로 전환이 가능합니다.

셋째, 기존 Recordset의 커서 타입에 관계없이 자유롭게 Disconnected 모델에서 사용이 가능합니다.

넷째, 기존 Legacy 로직을 XML 웹 서비스나 .NET 리모팅으로 쉽게 재사용할 수 있게 만들 수 있습니다.

 

단, 이렇게 할 경우, 재사용을 통해 시간과 비용을 절감할 수는 있겠지만, 성능적인 측면에서는 오히려 저하될 수도 있다는 것에 주의해야 합니다.

 

다음 번에는 ASP.NET 관련 팁일듯 합니다. ^^

 네번째로 ASP.NET 관련 팁 8가지 중 먼저 4가지를 올립니다.

 

Tip 8. 네트워크 드라이브에 있는 ASPX 실행하기

 

웹 서버가 여러 대 있는 경우, 웹 애플리케이션이 업데이트 되었을 때 이것을 각 웹 서버에 일일이 배포하는 일은 상당히 귀찮은 작업입니다. 별도의 배포 시스템(예 : AppCenter)을 사용하여 Rolling 업그레이드를 하는 것이 정석적입니다만, 어떤 회사에서는 배포 대상 파일을 한 곳(SAN과 같은 통합 스토리지, 파일 서버의 공유 폴더 등)에만 두고 이를 네트워크 드라이브로 공유해서 사용하는 경우도 있습니다. 이 경우, 공유된 경로에만 업데이트를 해주면 모든 웹 서버에 반영되므로 최소한 배포에 있어서는 훨씬 간편해 집니다.

 

 

우선 네트워크 경로로 IIS 홈 디렉터리를 설정하는 것은 쉽습니다.

 

문제는 ASP의 경우에는 별다른 문제가 없는데, ASP.NET의 경우, 위 그림과 같은 Parser 오류가 발생하게 됩니다. 무엇이 문제일까요?

바로 .NET Framework의 런타임 보안 정책과 관련된 문제입니다. ASP.NET이 ASPX 및 코드 비하인드 DLL을 처리하려면 FullTrust 권한이 필요합니다. 그런데, .NET Framework는 네트워크 드라이브, 즉 로컬 인트라넷에 대해서는 기본적으로 FullTrust를 하지 않기 때문에 이러한 문제가 발생하게 되는 것입니다.

해결책은 위의 글 중에 답이 있습니다. 런타임 보안 정책에서 로컬 인트라넷의 권한을 FullTrust로 변경해주면 됩니다.

 

Tip 9. 애플리케이션 간에 aspx, ascx 공유하기

 

예를 들어, 다음과 같이 동일 웹 애플리케이션 내에 있지 않는(즉, 다른 웹 애플리케이션에 속한) 웹 사용자 정의 컨트롤(*.ascx)을 페이지에서 사용하려고 하면, 다음과 같은 파서 오류가 발생되게 됩니다.

 

이 문제를 해결하려면, 다음과 같은 단계로 작업을 수행합니다.

1. 공유할 웹 애플리케이션을 동일한 물리적 위치로 지정합니다. 아래는 app1과 app2가 appshared를 공유할 경우입니다. (오타 수정 : 표에서 App1/control이 아니라 App1/appshared가 되어야 합니다)

 

2. 공유할 웹 애플리케이션(appshared)의 응용 프로그램 속성을 제거합니다.

3. 공유할 웹 애플리케이션(appshared)의 web.config, Global.asax를 제거합니다.

4. 이제 각 애플리케이션(App1, App2)의 페이지에서는 사용할 ASCX를 다음과 같이 지정합니다.

<%@ Register … Src="appshared/shareusercontrol.ascx" %>

 

Tip 10. 코드 비하인드의 긴급 수정

 

다음과 같은 시나리오를 가정해 보겠습니다.

ASP.NET 애플리케이션을 개발해서 개발 PC(또는 개발 서버)에 있던 내용을 실 서버에 배포했는데, 서버 관리자가 갑자기 긴급하게 수정해야 할 사항이 발견했습니다. 그런데 공교롭게도 이 내용은 매우 사소한 것이긴 한데, 코드 비하인드 클래스에 정의되어 있는 내용이라서 소스 파일을 수정해서 DLL을 재컴파일하여 올리지 않는 한 문제를 수정할 수 없는 상황이 되었습니다.

그런데, 이미 개발자는 퇴근을 했고, 관리자는 비주얼 스튜디오와 같은 개발 도구를 설치하지 않은 상황입니다. 비주얼 스튜디오가 있다고 하더라도 관리자는 사용법을 잘 알지 못합니다.

이러한 경우에 긴급하게 조치를 할 수 있는 방법이 없을까요?

 

우선 이 방법은 서버에 배포를 할 때, ASPX와 코드 비하인드 소스 파일(.aspx.cs 또는 .aspx.vb)이 같이 배포되어 있는 상황에서만 가능합니다. 또는 수정하려는 ASPX에 해당하는 코드 비하인드 소스 파일을 가지고 있어야 합니다.

이런 조건이 성립되어 있다고 가정하고, 긴급 조치 절차는 다음과 같습니다.

1. 수정할 .aspx의 코드 비하인드 소스 파일(.aspx.cs)를 편집기 등에서 열고, 문제점을 수정합니다.

2. 위의 코드 비하인드 클래스 이름을 임시로 변경합니다. 예를 들어 ProductList라면 ProductList_1 등으로 변경합니다.

3. 수정할 .aspx에서 <% @Page .. %> 내에서 CodeBehind를 Src로 바꾸고, Inherits 내의 클래스 이름을 1에서 변경한 이름으로 바꿉니다.

    <% @Page CodeBehind="productlist.aspx.cs" Inherits="Northwind.ProductList" %>

    <% @Page Src="productlist.aspx.cs" Inherits="Northwind.ProductList_1" %>

 

일단 이렇게 하면, 긴급 수정을 할 수 있게 됩니다. 그러나, 어디까지나 이 방법은 긴급 상황에서 사용할 수 있는 임시 방편일 뿐입니다. 정상적인 수정/업데이트의 경우에는 절대로 이 방법을 사용하지 않아야 합니다.

긴급 수정을 한 이후에는 개발자에게 해당 사항을 통지해주고, 문제점을 수정해서 컴파일된 버전으로 새로 업데이트를 하도록 합니다. 최소한 컴파일된 DLL을 업데이트해야 하고, 임시로 수정한 ASPX를 다시 원래로 돌려놓아야 한다는 것에 유의하십시오.

 

Tip 11. Postback 간 스크롤 위치 고정

 

웹 폼 페이지를 스크롤해서 내린 상태에서, 포스트 백이 일어나면 스크롤 위치가 다시 맨 상단으로 돌아가버리는 단점이 있습니다.

 

이 문제를 해결하기 위한 방법은 크게 3가지가 있습니다.

첫째, SmartNavigation을 사용하는 방법입니다. 다음과 같이 page directive에 SmartNavigation을 true로 지정해 줍니다.

<%@ Page ... SmartNavigation="true" %>

이 방법은 가장 간편하긴 하지만, 페이지 내에서 클라이언트 스크립트를 사용하는 경우 Smart Navigation의 스크립트와 충돌하여 문제가 발생할 수 있습니다.

 둘째, 다음과 같이 URL 뒤에 컨트롤ID를 앵커로 지정합니다.

http://localhost/.../....aspx#DataGrid1:_ctl72:_ctl0

위 코드는 DataGrid1의 72번째 줄을 기준으로 스크롤을 하게 됩니다.

 셋째, 클라이언트 스크립트의 scrollIntoView를 사용합니다.

<scirpt> document.all('DataGrid1').rows[72].scrollIntoView(true) </script>

 해결은 가능은 하지만, 보다시피 깔끔한 방법은 없네요. ^^ 

다음번엔 나머지 ASP.NET 팁 4가지를 올리겠습니다. 

네번째로 ASP.NET 관련 팁 8가지 중 나머지 4가지를 올립니다.

 

Tip 12. 폼 인증으로 모든 파일 보호하기

 

ASP.NET을 어느 정도 다뤄보신 분은 잘 아시겠지만, ASP.NET은 내부적으로 인증 메커니즘을 제공합니다. 특별한 코딩 작업 없이 web.config에 인증(Authentication)과 권한 부여(Authorization)만 지정하면 페이지 액세스 권한을 설정할 수 있습니다. 다음 예를 봅시다.

 

<authentication mode="Forms">
  <forms name="protectall" loginUrl="login.aspx" ... />
</authentication>

 

<authorization>
  <deny users="?" />
</authorization>

이렇게 web.config에 설정하면,

- 기본적으로 쿠키 기반의 폼 인증을 사용하여 사용자를 인증하며,

- 로그인 되지 않은 익명 사용자( = ?)에 대해서는 접근을 거부(deny)하며,

- login.aspx라는 페이지로 로그인하라고 리디렉션한다는 의미입니다.

 

그런데, 문제는 폼 인증의 경우, .aspx를 요청할 때만 동작한다는 것입니다. 만약 .gif와 같은 이미지 파일을 요청하면 어떻게 될까요?

해보면 알겠지만, 로그인을 하지 않았음에도 불구하고 .gif는 보이게 됩니다. 인증 자체를 Windows와 같은 다른 인증으로 바꾸면 .gif도 방어할 수 있지만, 폼 인증에서는 기본적으로 되지 않습니다.

폼 인증을 쓰면서 .gif에 대해서도 인증 및 권한부여 메커니즘을 동작시킬 수는 없을까요?

 

방법은 다음과 같이 간단하게 할 수 있습니다.

우선, IIS 관리자를 열고, 웹 애플리케이션(또는 사이트)의 홈 디렉터리 탭을 선택합니다. 하단의 응용 프로그램 설정에서 우측에 있는 구성 버튼을 누릅니다.

그러면, 아래 그림과 같이 응용 프로그램 구성 페이지가 나타납니다. 여기서 하단의 추가 버튼을 누르고 .gif에 대한 응용 프로그램 매핑을 추가하면 됩니다. 추가 시에는 .aspx와 동일한 실행 파일 경로(C:\WINDOWS\Microsoft.NET\Framework\[버전번호]\aspnet_isapi.dll)를 지정해주면 됩니다.

 

Tip 13. 스크롤 바를 가진 DataGrid 만들기

 

DataGrid를 페이지에서 표시하려고 하는데, DataGrid의 항목이 한 페이지에 표시하기에는 너무 많다고 가정합시다. 물론 페이징을 사용해서 처리하는 것도 방법이긴 하겠지만, 다음과 같이 스크롤을 하는 것이 보기 좋을 수도 있습니다..

 

일단 이걸 처리하는 방법은 간단합니다. <DIV> 내에 DataGrid를 집어넣고, <DIV>의 스타일에 overflow:auto를 지정해주면 됩니다. 물론 auto가 아닌 다른 값을 지정해줘도 됩니다.

그런데 이 방법의 문제는 DataGrid의 머리글(Header)까지 같이 스크롤되어 버린다는 문제점이 있습니다. 우리가 원하는 것은 머리글은 그대로 있고, DataGrid의 항목만 스크롤이 되는 다음과 같은 형태가 되겠죠.

 

사실 이렇게 만드는 방법은 민망할 정도의 꽁수에 불과합니다. 어떻게 하면 되느냐 하면..

DataGrid에서 머리글을 표시하지 않도록 지정하고, 머리글은 Table 태그를 사용해서 직접 그리는 것입니다. 그릴 때는 DataGrid 안의 테이블과 크기를 잘 조정해야 하고, DataGrid를 포함하고 있는 <DIV> 바로 위에 지정해야 한다는 것에 주의하십시오.

 

<!-- 머리글 내용 -->

<Table>...</Table>

<DIV style="overflow:auto">

   <asp:DataGrid ...>

       ...

   </asp:DataGrid>

</DIV>

 

Tip 14. 서버에서 클라이언트 스크립트 처리

서버 사이드 코드에서 클라이언트 스크립트를 처리하는데는 대략 다음과 같은 시나리오가 있습니다.

- 클라이언트측 스크립트 라이브러리를 제공하는 시나리오
- 페이지 상단이나 하단에 스크립트를 생성하는 시나리오
- (페이지에 여러 개의 컨트롤 인스턴스가 존재하더라도) 페이지에 스크립트 블록이 단 한번만 나타나도록 확인하는 시나리오
- 페이지에 폼이 포함된 경우, 폼의 클라이언트측 제출 이벤트와 이벤트 처리기를 연결하도록 컨트롤을 설정하는 시나리오
- 컨트롤에 의해 렌더링된 클라이언트측 요소를 클라이언트 상에서 선언된 배열 변수에 추가하는 시나리오
- 숨겨진 필드를 통한 값 전달

- 서버 컨트롤이 렌더링된 후 컨트롤의 클라이언트 이벤트와 이벤트 핸들러 연결 

 

Page 클래스는 다양한 Register* 계열의 메서드를 통해 이러한 시나리오를 가능하게 해줍니다. 관련된 내용은 MSDN의 다음 URL을 참조하기 바랍니다.

http://msdn.microsoft.com/library/KOR/cpguide/html/cpconClient-sideFunctionalityInServerControl.asp

 

위의 붉은색 부분에 대응되도록 작성한 코드는 다음과 같습니다.

 

 private void Page_Load(object sender, System.EventArgs e)
 {
      String scriptString = "\n";
      scriptString += "<script language=JavaScript>\n";
      scriptString += "<" + "!--\n";
      scriptString += "    function showIds() {\n";
      scriptString += "        for(var index=0;index < ids.length;index++)\n";
      scriptString += "        document.write(ids[index] + '<br>');\n";
      scriptString += "    }\n";
      scriptString += "//-->\n";
      scriptString += "<" + "/" + "script>\n";
   
      RegisterStartupScript("arrayScript", scriptString);
     
      string[] ids = {"111","112","the third one","114"};
      RegisterArrayDeclaration("ids","'" + String.Join("','",ids) + "'");

      RegisterHiddenField("myHidden", ".netXpert");
       
      btnArray.Attributes.Add("onClick", "showIds()");
      btnHidden.Attributes.Add("onClick", "alert(document.all.myHidden.value); return false;");
 }

 

이 코드를 사용해서 렌더링된 HTML은 다음과 같습니다.

 

Tip 15. 웹 팜(Web Farm) 환경에서 상태 관리 문제

 

L4나 NLB를 사용하여 여러 대의 웹 서버를 묶어서 동작시키는 웹 팜(Web Farm) 환경에서 ASP.NET을 사용하는 경우, 상태 관리 문제가 이슈가 됩니다. 대표적인 것이 ViewState와 Session, 폼 인증 쿠키 등이 있겠습니다.

 예를 들어 ViewState와 관련하여 PostBack 시에 다음과 같은 에러 메시지가 발생할 수 있습니다.

 HttpException (0x80004005): The View State is invalid for this page and might be corrupted.]
   System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +150
   System.Web.UI.Page.LoadPageViewState() +16
   System.Web.UI.Page.ProcessRequestMain() +421
….

 

아마 한글 메시지로는 '이 페이지의 뷰 상태가 유효하지 않거나 훼손되었습니다.'라는 걸로 나오던 걸로 기억합니다.

 이 문제가 발생하는 원인을 살펴보겠습니다.

웹 팜 환경에서는 최초 페이지 접근은 A 서버로 하고, PostBack은 B 서버로 날라갈 수도 있습니다.

PostBack 동안 ViewState가 페이지에서 저장/로드될 때는 특정한 키를 사용하여 Encrypt/Decrypt 되는 과정을 거치게 됩니다. 그런데, 이때 사용하는 키 값이 A 서버와 B 서버가 서로 다르게 설정되어 있을 것이기 때문에 위와 같은 에러가 발생하게 됩니다.

폼 인증 사용 시 쿠키와 관련한 문제도 이것과 동일합니다.

 

이 문제를 해결할 수 있는 방법은 크게 2가지입니다.

우선 첫번째는 L4 장비에서 세션이 유지되도록(즉, 한 사용자는 최초 접근한 서버로 계속 접근하도록) 설정하는 방법이 있습니다. 이 방법을 사용하면 상태 관리 문제(세션변수, ViewState, 폼인증 쿠키)를 모두 잊어버려도 된다는 장점이 있는 대신에, 로드 밸런싱의 효율성이 떨어지게 됩니다.

그래서 권장되는 두번째 방법은 바로 모든 웹 서버의 키 값을 동일하게 설정해주는 것입니다. 키를 생성하고, machine.config를 수정하는 부분에 대해서는 다음 KB를 참고하십시오.

 http://support.microsoft.com/default.aspx?scid=312906

 그러나, 세션변수(Session)의 경우에는 두번째 방법으로 해결되지 않습니다. 세션 변수는 페이지에 저장되는 것이 아니라 웹 서버의 메모리에 저장되는 것이기 때문입니다. L4 설정을 하지 않고 세션 변수를 공유하는 방법은 없을까요? 바로 ASP.NET의 StateServer나 SqlServer 모드 등을 통해 세션을 별도로 관리하는 방법이 있습니다. 여기에 관해서는 다음 URL을 참조하십시오.

 http://msdn.microsoft.com/library/KOR/cpguide/html/cpconSessionState.asp

 웹 팜에서 StateServer나 SqlServer 모드를 사용할 때는 주의해야 할 사항이 하나 있습니다. 다음 URL을 반드시 참고하시기 바랍니다.

 http://support.microsoft.com/default.aspx?scid=325056

 마지막으로 Session 변수와 ViewState에 대해 참고할 만한 좋은 문서가 있습니다. ASP.NET Program Manager인 Susan Warren이 작성한 다음 문서를 보기 바랍니다.

 http://msdn.microsoft.com/library/en-us/dnaspnet/html/asp11222001.asp

 이 팁 시리즈도 이제 다음 번이 마지막이 될 것 같네요.

출처: http://blog.naver.com/saltynut


쌈꼬쪼려 소백촌닭
Posted by SB패밀리

private void Page_Load(object sender, System.EventArgs e)
{
    string fullName = "SampleProject.Resources.AppResources";

    System.Reflection.Assembly Assem = System.Reflection.Assembly.GetExecutingAssembly();
    ResourceManager appResourceMgr = new ResourceManager(fullName, Assem);

    Button1.Text = appResourceMgr.GetString("Msg.Click");
    Label1.Text = appResourceMgr.GetString("Msg.Welcome");
}

 

 

실제 Resx 파일로부터 데이터를 가져오기 위해서 필요한 핵심 클래스는 바로 ResourceManager 입니다. 이 친구가 특정 resx 파일로부터 우리가 원하는 데이터를 뽑아올 수 있도록 도와주는 클래스이거든요. 이 클래스의 더욱 구체적인 설명은 MSDN에서 한번 찾아보시길 권해드립니다.

일단, ResourceManager 클래스는요. 사용법이 간단합니다. 클래스의 인스턴스를 만들면서 생성자에 2가지의 매개변수를 지정해 주시면 되거든요. 즉, 우리가 사용하려는 리소스 파일의 논리적인 이름(전체 이름)을 첫번째 인자로 그리고, 사용하려는 리소스 파일이 들어있는 어셈블리를 두번째 인자로 지정해 주시면 되는 것입니다.

그렇다면, 우리가 사용하려는 리소스 파일의 논리적인 이름 즉, 루트 이름은 어떻게 알 수 있을까요? 이 이름은 네임스페이스를 포함한 리소스의 명칭을 말하는 것이잖습니까? 그렇기에, 일단 그 명칭의 시작은 프로젝트의 네임스페이스인 SampleProject. 으로 시작하겠죠? 그리고, 현재 Resources 라는 폴더안에 들어있으므로, 완전한 명칭은 SampleProject.Resources.AppResources이 될 것입니다.

물론, 이것은 C#인 경우 그런 것이구요. VB.NET으로 프로젝트를 만드셨다면 이야기가 약간 달라집니다. 왜냐하면, C#은 기본적으로 컴파일 시에, 폴더가 있으면 그 폴더 이름을 네임스페이스에 추가해서 달고 가는 반면, VB.NET은 그 폴더명을 무시하거든요. 즉, 현재의 경우 C# 프로젝트이고, 우리가 사용하려는 리소스 파일이 Resources 폴더 안에 있으니,

SampleProject.Resources.AppResources

가 완전한 리소스 파일의 논리적인 이름이 되겠지만, VB.NET의 경우는 폴더명을 무시하기에

SampleProject.AppResources

가 된다는 것입니다. ^^;; (맨 뒤에 .resx가 없는 것에 주의하세요 ^^)

 

근데, 왜 그림에서는 경로의 마지막에 .resources 라는 것이 붙어있냐고 물어보시려 하시는구려~

그것은 이것이 리소스 파일이기 때문에 붙여지는 것이랍니다. 원래 우리눈에 보기에 .resx 였던 것의 본명이 제대로 붙여진 것이지요 ^^;; 하지만, ResourceManager 클래스를 이용해서 리소스 파일을 로드하는 경우에는 전체 경로에서 .resources 라는 부분은 무시하셔도 됩니다. 왜냐하면, ResourceManager 를 이용하면 이 친구는 알아서 .resources로 끝나는 리소스들을 찾게 되니까요 ^^

자. 장황하긴 했지만 특정 리소스 파일의 논리적인 루트경로를 어떻게 알아낼 수 있는지 이야기를 해 보았습니다. 이제 다시 원래의 소스코드로 돌아가서 이야기를 계속해 보겠습니다. 우리의 소스 코드의 두번째 라인은 다음과 같았습니다.

System.Reflection.Assembly Assem = System.Reflection.Assembly.GetExecutingAssembly();

이 코드는 무엇을 의미하는 것일까요? 이는 .NET 리플렉션이라는 기법을 이용하고 있는 것인데요. 이 코드의 의미는 바로 현재 구동중인 어셈블리 자체를 얻어오는 것입니다. 우리가 사용하고자 하는 리소스 파일이 현재 실행되고 있는 어셈블리에 들어있으므로, 이를 .NET 리플렉션을 이용해서 얻어오는 것이죠. 말은 어려울지 모르지만 코드는 매우 간단합니다.

즉, System.Reflection.Assembly.GetExecutingAssembly() 라는 코드를 이용하시면 현재 실행중이 DLL이나 EXE 어셈블리 자체를 얻어올 수가 있게 됩니다. 왜 이 어셈블리를 얻어오냐구요? 음.... 앞에서 집중을 조금 덜 하셨었군여!!!

ResourceManager 클래스의 생성자가 요구하는 두번째 인자라 뭐라고 했었죠? 바로 사용하려는 리소스 파일이 들어있는 어셈블리라고 했었죠? 그렇습니다. 그렇기 때문에 이렇게 어셈블리를 얻어오는 것입니다. ^^

 

 

참고 : 리플렉션이란?

MSDN을 참고해 보면 말입니다. MSDN에서는 리플렉션을 다음과 같이 설명하고 있습니다.

리플렉션을 사용하여 형식의 인스턴스를 동적으로 만들거나, 형식을 기존 개체에 바인딩하거나, 기존 개체에서 형식을 가져올 수 있습니다. 그리고 나서 형식의 메서드를 호출하거나 형식의 필드 및 속성에 액세스할 수 있습니다. 리플렉션의 일반적인 용도는 다음과 같습니다.

Assembly를 사용하여 어셈블리를 정의 및 로드하고, 어셈블리 매니페스트에 나열된 모듈을 로드하며, 이 어셈블리에서 형식을 찾아 형식의 인스턴스를 만듭니다.

MethodInfo를 사용하여 메서드의 이름, 반환 형식, 매개 변수, public 또는 private 같은 액세스 한정자, 추상 또는 가상 같은 구현 정보를 검색합니다. Type의 GetMethods 또는 GetMethod 메서드를 사용하여 특정 메서드를 호출합니다.

FieldInfo를 사용하여 필드의 이름, 액세스 한정자, public 또는 private 같은 액세스 한정자, static 같은 구현 정보를 검색하고, 필드 값을 가져오거나 설정합니다.

.... (이후 생략)

* 위의 글은 MSDN(VS.NET 한글 도움말)에서 인용한 것임을 밝힙니다.

 

이제, 필요한 두가지 인자가 모두 준비되었으니 ResourceManager 클래스의 인스턴스를 생성해 보겠습니다. 코드의 3번째 라인이 바로 그 작업을 하고 있죠?

ResourceManager appResourceMgr = new ResourceManager(fullName, Assem);

그렇습니다. 이것으로 모든 준비는 끝난 것입니다. 이제 appResourceMgr 라는 변수명을 이용해서 맘껏 리소스 파일안의 데이터들을 불러올 수가 있게 된 것입니다.

데이터를 불러오기 위해서는 꼴랑~ GetString라는 메서드를 사용해 주시면 됩니다. ^^ 이미지나 이진 데이터를 불러오기 위해서 GetObject라는 메서드도 이용할 수 있긴 합니다만, 현업에서는 주로 문자열 메시지들을 담게 될 것이기에... GetString 만을 기억하고 계셔도 무난합니다 ^^.. 예를 들면, 다음 코드처럼 말이죠

Button1.Text = appResourceMgr.GetString("Msg.Click");

GetString 메서드의 인자로는 불러오고자 하는 메시지의 키 값을 적어주시면 됩니다. 그러면 그에 해당하는 값을 뽑아낼 수 있는 것이죠 ^^;; 쉽지 않습니까?

뭔가 초기 세팅이 복잡해 보일 수도 있는데요. 한번 해보고 나면 한개두 안 복잡합니다.

그리고, 컴포넌트 개발에 익숙하신 분이라면 위의 코드 중 공통적인 부분을 따로 빼서... 별도의 헬퍼 클래스를 만들어서 ResourceManager를 좀 더 사용하기 쉽게 만드실 수도 있겠네요 ^^

Posted by SB패밀리

코딩 기술에는 소프트웨어 개발의 여러 가지 측면이 통합되어 있습니다. 일반적으로 코딩 기술은 응용 프로그램의 기능에 영향을 미치지는 않지만, 소스 코드의 가독성을 향상시키는 데 기여합니다. 여기서는 프로그래밍, 스크립팅, 태그 및 쿼리 언어와 같은 모든 형식의 언어가 고려됩니다.

여기에서 정의된 코딩 기술은 고정된 집합의 코딩 표준을 구성하는 데는 사용하지 않는 것이 좋습니다. 그보다는 특정 소프트웨어 프로젝트에 사용되는 코딩 표준을 개발하기 위한 지침으로 사용하는 것이 좋습니다.

코딩 기술은 다음 세 단원으로 구분됩니다.

이름

명명 스키마는 응용 프로그램의 논리적 흐름을 이해할 수 있는 가장 강력한 수단 중의 하나입니다. 이름에는 "어떻게"보다는 "무엇을"이 나타나야 합니다. 내부 구현을 노출시키는 이름을 사용하지 않으면 복잡성이 줄어든 추상화 계층을 유지할 수 있습니다. 예를 들어, GetNextArrayElement() 대신 GetNextStudent()를 사용할 수 있습니다.

올바른 이름을 선택하는 것이 어렵다는 것은 사용자가 항목의 목적을 더 정확하게 분석하고 정의할 필요가 있다는 것을 의미할 수 있습니다. 이름의 길이는 정확한 의미를 전달할 수 있을 정도로 길어야 하지만 너무 길어져서 의미가 모호해지면 안됩니다. 프로그램 상에서 고유 이름은 특정 항목과 다른 항목을 구분하는 용도로만 사용되며, 표현 이름의 기능은 사용자의 이해를 돕는 것입니다. 따라서 사용자가 이해할 수 있는 이름을 제공하는 것이 좋습니다. 하지만 선택된 이름은 해당 언어의 규칙과 표준을 준수해야 합니다.

다음은 명명 기술에서 권장되는 사항입니다.

루틴

  • 루틴 이름 AnalyzeThis()와 변수 이름 xxK8과 같이 주관적으로 해석될 수 있는 모호한 이름은 사용하지 않습니다. 이와 같은 이름은 추상화에 기여하기 보다 모호함의 원인이 됩니다.
  • 개체 지향 언어에서 클래스 속성 이름에 Book.BookTitle과 같은 클래스 이름을 포함시키는 것은 불필요합니다. 그 대신 Book.Title을 사용합니다.
  • 지정 개체에서 특정 동작을 수행하는 루틴을 명명하기 위해서는 CalculateInvoiceTotal()와 같은 동사-명사 방식을 사용합니다.
  • 함수 오버로딩이 허용되는 언어에서 모든 오버로드는 유사한 동작을 수행해야 합니다. 함수 오버로딩이 허용되지 않는 언어에서는 유사한 기능을 관련시켜 주는 명명 표준을 설정합니다.

변수

  • 해당하는 변수 이름의 끝에 Avg, Sum, Min, Max, Index와 같은 계산 한정자를 추가합니다.
  • 변수 이름에 min/max, begin/end 및 open/close와 같은 보완적인 쌍을 사용합니다.
  • 대부분의 이름은 연속되는 몇 개의 단어로 구성되므로, 대소문자를 혼용하여 쉽게 읽을 수 있도록 합니다. 또한 변수와 루틴을 쉽게 구별하기 위해 루틴 이름에는 각 단어의 첫 글자가 대문자로 시작되는 파스칼 대/소문자(CalculateInvoiceTotal)를 사용합니다. 변수 이름에는 첫 단어를 제외한 각 단어의 첫 글자가 대문자로 시작하는 카멜 대/소문자(documentFormatType)를 사용합니다.
  • 부울 변수 이름에는 fileIsFound와 같이 Is가 포함되어야 합니다. Is는 Yes/No 또는 True/False 값을 내포합니다.
  • 상태 변수를 명명하는 경우에는 Flag와 같은 구문을 사용하지 않습니다. 이 구문은 두 개 이상의 값을 가질 수 있다는 점에서 부울 변수와 다릅니다. documentFlag를 사용하는 대신 documentFormatType와 같이 더 정확한 이름을 사용합니다.
  • 단 몇 줄의 코드에서만 사용되는 임시 변수에 대해서도 의미 있는 변수를 사용하십시오. i 또는 j와 같은 단일 글자 변수 이름은 단기 루프 인덱스에만 사용합니다.
  • For i = 1 To 7과 같은 리터럴 숫자나 리터럴 문자열은 사용하지 않습니다. 그 대신 For i = 1 To NUM_DAYS_IN_WEEK와 같은 명명된 상수를 사용하여 관리와 이해를 쉽게 합니다.

테이블

  • 테이블을 명명하는 경우에는 이름을 단수로 표현합니다. 예를 들어, Employees 대신 Employee를 사용합니다.
  • 테이블 열을 명명하는 경우에는 테이블 이름을 반복하지 않습니다. 예를 들어, Employee라는 테이블에서 EmployeeLastName이라는 필드를 사용하지 않습니다.
  • 열 이름에 데이터 형식을 포함시키지 않습니다. 이렇게 하면 나중에 데이터 형식을 변경할 때 필요한 작업의 양이 줄어들게 됩니다.

Microsoft SQL Server

  • 저장 프로시저 앞에 sp를 붙이지 마십시오. sp는 시스템 저장 프로시저를 식별하기 위해 예약된 접두사입니다.
  • 사용자 정의 함수 앞에 fn_을 붙이지 마십시오. fn_은 기본 제공 함수를 식별하기 위해 예약된 접두사입니다.
  • 확장 저장 프로시저 앞에 xp_를 붙이지 마십시오. xp_는 시스템 확장 저장 프로시저를 식별하기 위해 예약된 접두사입니다.

기타

  • 가능하면 약어를 사용하지 않습니다. 다만 사용자가 일관성 있게 만든 약어는 사용합니다. 약어에는 단 한 가지 의미만 존재해야 하며, 각 약자에는 단 하나의 약어가 존재해야 합니다. 예를 들어, minimum의 약어로 min을 사용하는 경우 다른 곳에서 minute의 약자로 min을 사용해서는 안됩니다.
  • 함수를 명명하는 경우 GetCurrentWindowName()과 같이 반환되는 값에 대한 설명을 포함시킵니다.
  • 프로시저 이름과 마찬가지로 파일과 폴더 이름도 그 용도를 정확하게 나타내야 합니다.
  • ProcessSales()라는 루틴과 iProcessSales라는 변수와 같이 서로 다른 요소를 위해 이름을 반복 사용하지 않습니다.
  • 코드 검사 시 혼동을 방지하려면 요소를 명명할 때 write 및 right와 같은 동음 이의어를 사용하지 않습니다.
  • 요소를 명명하는 경우 철자가 자주 틀리는 단어를 사용하지 않습니다. 또한 color/colour 및 check/cheque와 같이 지역마다 철자가 다르다는 점도 알아 둡니다.
  • 데이터 형식을 식별하기 위해 입력 마크(예: 문자의 $, 정수의 %)를 사용하지 않습니다.

주석

소프트웨어 설명서는 외부와 내부 설명서의 두 가지 형태로 존재합니다. 사양, 도움말 파일 및 디자인 문서와 같은 외부 설명서는 소스 코드의 외부에서 관리되며, 내부 설명서는 개발자가 개발 시에 소스 코드 내부에서 작성한 주석들로 구성됩니다.

외부 설명서를 사용할 수도 있지만, 하드 카피 설명서를 잊어 버릴 수 있으므로 소스 코드 목록을 독자적으로 유지해야 합니다. 외부 설명서는 사양, 디자인 문서, 변경 요청, 버그 목록 및 사용되는 코딩 표준으로 구성되어야 합니다.

내부 소프트웨어 설명서의 한 가지 문제는 소스 코드와 일치하도록 주석을 관리하고 업데이트하는 것입니다. 적절한 주석이 추가된 소스 코드는 실행 시에 특별한 역할을 하지 않지만, 상당히 복잡하고 귀찮은 소프트웨어를 관리해야 하는 개발자에게 있어서는 상당히 중요한 역할을 합니다.

다음은 주석 추가 시 권장되는 사항입니다.

  • C#에서 개발을 수행하는 경우 XML 문서 기능을 사용합니다. 자세한 내용은 XML 문서를 참조하십시오.
  • 코드를 수정하는 경우에는 반드시 최신 주석을 코드에 추가하십시오.
  • 모든 루틴의 시작부에는 이 루틴의 목적, 전제, 제한 등을 나타내는 정확한 표준 주석을 추가하는 것이 도움이 됩니다. 정확한 주석은 이 루틴의 용도와 목적을 간략하게 설명할 수 있어야 합니다.
  • 코드 줄의 끝에 주석을 추가하지 않습니다. 끝부분에 주석이 있으면 코드를 읽기가 더 어렵습니다. 하지만 변수 선언에 대해 주석을 추가하는 경우에는 줄 끝에 주석을 추가하는 것이 좋습니다. 이 때 탭을 사용하여 모든 주석을 일렬로 정렬합니다.
  • 모든 줄에 별표가 사용된 혼란스러운 주석을 사용하지 않습니다. 그 대신 공백을 사용하여 주석과 코드를 구분합니다.
  • 입력 프레임을 사용하여 블록 주석을 둘러싸지 않습니다. 이렇게 하면 멋지게 보일 수는 있지만 관리가 어려워집니다.
  • 나중에 관리 작업을 수행하는 동안 혼동을 방지하려면 임시 주석이나 관련이 없는 주석을 완전히 제거한 후에 배포합니다.
  • 복잡한 코드 부분을 설명하는 주석이 필요한 경우 그 코드를 검사하여 이 주석을 다시 작성해야 하는지를 결정합니다. 가능하면 오류 코드를 제공하지 말고 다시 작성합니다. 일반적으로 코드를 단순하게 만든다고 해서 성능이 저하되는 것은 아니지만, 성능과 유지 관리성 사이에 균형을 유지해야 합니다.
  • 주석을 작성하는 경우 완벽한 구문을 사용하십시오. 주석은 코드를 모호하게 하는 것이 아니라 명확하게 하는 것입니다.
  • 나중에 따로 주석을 추가할 시간이 없으므로 코딩과 주석 작업을 동시에 수행하십시오. 또한 자신이 작성했던 코드를 다시 살펴보아야 합니다. 지금은 코드가 명확하게 이해되지만 6주 후에는 명확하지 않을 수도 있습니다.
  • 유머스러운 말과 같은 불필요하고 부적절한 주석을 사용하지 않습니다.
  • 주석을 사용하여 코드의 목적을 설명하십시오. 주석은 코드를 그대로 옮겨 놓은 것이 아닙니다.
  • 코드에서 쉽게 알아볼 수 없는 모든 내용을 주석에 추가합니다.
  • 반복되는 문제를 해결하려면 버그 수정과 작업 코드에 대해 주석을 사용합니다.
  • 루프와 논리 분기로 이루어진 코드에 대해 주석을 사용합니다. 이 주석은 소스 코드 사용자에게 도움이 되는 중요한 부분입니다.
  • 응용 프로그램 전체에서 일관적인 문장 부호와 구조로 일정한 스타일을 사용하여 주석을 작성합니다.
  • 공백을 사용하여 주석 구분 기호와 주석을 분리합니다. 이렇게 하면 색 표시를 사용하지 않고 코드를 보는 경우에도 쉽고 명확하게 주석을 볼 수 있습니다.

서식

서식은 코드의 논리적 구조를 명확하게 합니다. 소스 코드의 서식을 일관적이고 논리적인 방식으로 적용하면 자신이나 다른 개발자가 이 소스 코드를 해독하는 데 도움이 됩니다.

다음은 서식 기술에서 권장되는 사항입니다.

  • 들여쓰기의 표준 크기(예: 공백 4개)를 설정하고 이 크기를 일관적으로 사용합니다. 지정된 들여쓰기를 사용하여 코드부를 정렬합니다.
  • 하드 카피 형태로 소스 코드를 출판하려면 한 가지 글꼴을 사용합니다.
  • 중괄호 쌍이 함께 나타나는 경우에는 다음과 같이 여는 중괄호와 닫는 중괄호를 수직으로 정렬합니다.
    for (i = 0; i < 100; i++)
    {
       ...
    }

    여는 중괄호가 줄의 끝에 나타나고 닫는 중괄호가 줄의 앞에 나타나는 경우에는 다음과 같은 스타일을 사용할 수 있습니다.

    for (i = 0; i < 100; i++){
       ...
    }

    선택된 스타일을 소스 코드 전체에서 일관적으로 사용합니다.

  • 논리적 구조에 따라 코드를 들여쓰기합니다. 들여쓰기를 사용하지 않으면 다음과 같이 코드를 이해하기가 어렵습니다.
    If ... Then
    If ... Then
    ...
    Else
    End If
    Else
    ...
    End If

    코드를 들여쓰면 다음과 같이 코드를 쉽게 이해할 수 있습니다.

    If ... Then
       If ... Then
       ...
       Else
       ...
       End If
    Else
    ...
    End If
  • 소스 코드 편집기의 스크롤을 방지하고 하드 카피가 명확하게 표시되도록 하기 위해 주석과 코드의 최대 줄 길이를 설정합니다.
  • 코드의 목적이 변경되지 않는 범위 내에서 모든 연산자의 전후에 공백을 사용합니다. 예를 들어, 한 가지 예외로는 C++에서 사용되는 포인터 표기법이 있습니다.
  • 공백을 사용하여 소스 코드의 구조를 체계적으로 표시할 수 있습니다. 이렇게 하면 코드 "단락"을 만들 수 있으므로, 사용자는 소프트웨어의 논리적 구조를 쉽게 이해할 수 있습니다.
  • 하나의 코드가 여러 줄에 걸쳐 나타나는 경우, 다음 줄이 없으면 코드가 불완전하다는 사실을 명확히 하기 위해 연결 연산자를 각 줄의 끝에 추가합니다.
  • 한 줄에 두 개 이상의 문을 사용하지 않습니다. 한 가지 예외로는 C, C++, C# 또는 JScript 에서 사용되는 for (i = 0; i < 100; i++)와 같은 루프가 있습니다.
  • HTML을 작성하는 경우 태그와 특성의 표준 서식을 설정합니다. 예를 들어 태그는 모두 대문자로 설정하고 특성은 모두 소문자로 설정할 수 있습니다. 다른 방법으로는 XHTML 사양을 사용하여 모든 HTML 문서가 유효하도록 보장할 수 있습니다. 웹 페이지를 만드는 경우 파일의 크기가 문제될 수 있지만, 따옴표 붙은 특성값과 닫는 태그를 사용하면 쉽게 관리할 수 있습니다.
  • SQL 문을 작성하는 경우 모든 키워드에는 대문자를 사용하고 테이블, 열 및 뷰와 같은 데이터베이스 요소에는 대소문자를 혼용합니다.
  • 실제 파일 사이에서 소스 코드를 논리적으로 분할합니다.
  • 다음과 같이 각각의 주 SQL 절을 별도의 줄에서 작성하면 쉽게 코드를 읽고 편집할 수 있습니다.
    SELECT FirstName, LastName
    FROM Customers
    WHERE State = 'WA'
  • 길고 복잡한 코드부는 쉽게 이해할 수 있는 작은 모듈로 분할합니다
Posted by SB패밀리

네임스페이스:  :Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.aspx">System.Globalization
.NET Framework 클래스 라이브러리
DateTimeFormatInfo 클래스
[SerializableAttribute]
[ComVisibleAttribute(true)]
public sealed class DateTimeFormatInfo : ICloneable, 
    IFormatProvider

 

이 클래스에는 날짜 패턴, 시간 패턴 및 AM/PM 지정자와 같은 정보가 들어 있습니다.

특정 문화권에 대한 DateTimeFormatInfo를 만들려면 응용 프로그램에서 해당 문화권에 대한 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl72',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.aspx">CultureInfo를 만든 다음 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl73',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.datetimeformat.aspx">CultureInfo..::.DateTimeFormat 속성을 검색합니다. 현재 스레드의 문화권에 대한 DateTimeFormatInfo를 만들려면 응용 프로그램에서 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl75',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.currentinfo.aspx">CurrentInfo 속성을 사용해야 합니다. 고정 문화권에 대한 DateTimeFormatInfo를 만들려면 응용 프로그램에서 읽기 전용 버전의 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl76',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.invariantinfo.aspx">InvariantInfo 속성 또는 쓰기 가능한 버전의 DateTimeFormatInfo 생성자를 사용합니다. 중립 문화권에 대한 DateTimeFormatInfo를 만들 수 없습니다.

사용자는 제어판의 국가 및 언어 옵션을 통해 Windows의 현재 문화권과 연결된 값의 일부를 재정의할 수 있습니다. 예를 들어, 날짜를 다른 형식으로 표시하거나 해당 문화권의 기본 통화 단위가 아닌 다른 통화 단위를 사용할 수 있습니다. :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl77',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.useuseroverride.aspx">CultureInfo..::.UseUserOverride 속성을 true로 설정한 경우 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl79',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.datetimeformat.aspx">CultureInfo..::.DateTimeFormat 인스턴스, :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl81',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.numberformat.aspx">CultureInfo..::.NumberFormat 인스턴스 및 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl83',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.textinfo.aspx">CultureInfo..::.TextInfo 인스턴스의 속성 또한 사용자 설정에서 검색됩니다. 사용자 설정이 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl85',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.aspx">CultureInfo에 연결된 문화권과 호환되지 않는 경우(예: 선택한 달력이 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl86',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.optionalcalendars.aspx">OptionalCalendars에서 나타내는 달력 중 하나가 아닌 경우) 메서드의 결과와 속성 값은 정의되지 않습니다.

.NET Framework 버전 2.0 이전에서는 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl87',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.cultureinfo.useuseroverride.aspx">CultureInfo..::.UseUserOverride 속성이 true로 설정된 경우 사용자가 재정의할 수 있는 속성에 처음으로 액세스한 경우에만 개체에서 해당 속성을 읽습니다. DateTimeFormatInfo에 사용자가 재정의할 수 있는 속성이 둘 이상 있으므로 "초기화 지연"을 사용하면 응용 프로그램에서 한 속성에 액세스한 다음 사용자가 다른 문화권으로 변경하거나 제어판의 국가 및 언어 옵션을 통해 현재 사용자 문화권의 속성을 재정의한 후 응용 프로그램에서 다른 속성에 액세스할 때 이러한 속성이 일치하지 않게 될 수 있습니다. 예를 들어 이와 같은 순서로 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl89',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.longdatepattern.aspx">LongDatePattern에 액세스한 다음 사용자가 제어판에서 패턴을 변경한 후 다시 액세스하면 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl90',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.shortdatepattern.aspx">ShortDatePattern에 새 설정이 적용됩니다. 사용자가 특정 패턴을 재정의하는 대신 다른 사용자 문화권을 선택하는 경우에도 이와 유사한 불일치가 발생할 수 있습니다.

.NET Framework 버전 2.0 이상에서 DateTimeFormatInfo는 이 "초기화 지연"을 사용하지 않고 생성될 때 사용자가 재정할 수 있는 속성을 모두 읽습니다. 개체 만들기도 사용자 재정의 프로세스도 모두 원자성이 아니고 개체를 만드는 중에 관련 값이 변경될 수 있으므로 여전히 취약한 부분이 있지만 이는 매우 드문 경우입니다.

이 변경 사항은 serialization의 경우에 특히 중요합니다. ..NET Framework 버전 2.0 이상에서는 serialization이 발생되는 시간까지 액세스되는 설정뿐 아니라 재정의 가능한 모든 설정이 유지됩니다.

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl91',this);" href="http://msdn.microsoft.com/ko-kr/library/system.datetime.aspx">DateTime 값의 형식은 DateTimeFormatInfo 속성에 저장된 표준 또는 사용자 지정 패턴을 사용하여 지정됩니다.

응용 프로그램에서는 쓰기 가능한 DateTimeFormatInfo 개체의 관련 속성을 설정하여 표준 패턴을 사용자 지정 패턴으로 대체할 수 있습니다. :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl92',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.isreadonly.aspx">IsReadOnly 개체가 쓰기 가능한지 확인하려면 응용 프로그램에서 DateTimeFormatInfo 속성을 사용해야 합니다.

다음 표에서는 DateTimeFormatInfo 속성과 관련된 표준 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl93',this);" href="http://msdn.microsoft.com/ko-kr/library/system.datetime.aspx">DateTime 형식 패턴을 보여 줍니다. 자세한 내용은 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl94',this);" href="http://msdn.microsoft.com/ko-kr/library/az4se3k1.aspx">표준 날짜 및 시간 형식 문자열을 참조하십시오.

형식 패턴

관련 속성/설명

d

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl95',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.shortdatepattern.aspx">ShortDatePattern

D

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl96',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.longdatepattern.aspx">LongDatePattern

f

전체 날짜 및 시간(자세한 날짜와 간단한 시간)

F

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl97',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.fulldatetimepattern.aspx">FullDateTimePattern(자세한 날짜와 자세한 시간)

g

일반(간단한 날짜와 간단한 시간)

G

일반(간단한 날짜와 자세한 시간)

m, M

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl98',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.monthdaypattern.aspx">MonthDayPattern

o, O

라운드트립 날짜/시간 패턴. 이 형식 패턴을 사용하면 형식 지정 또는 구문 분석 작업을 할 때 항상 고정 문화권이 사용됩니다.

r, R

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl99',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.rfc1123pattern.aspx">RFC1123Pattern. 이 형식 패턴을 사용하면 형식 지정 및 구문 분석 작업을 할 때 항상 고정 문화권이 사용됩니다.

s

지역 시간을 사용한 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl100',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.sortabledatetimepattern.aspx">SortableDateTimePattern(ISO 8601 기반). 이 형식 패턴을 사용하면 형식 지정 또는 구문 분석 작업을 할 때 항상 고정 문화권이 사용됩니다.

t

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl101',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.shorttimepattern.aspx">ShortTimePattern

T

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl102',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.longtimepattern.aspx">LongTimePattern

u

세계 표준시 표시 형식을 사용한 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl103',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.universalsortabledatetimepattern.aspx">UniversalSortableDateTimePattern. 이 형식 패턴을 사용하면 형식 지정 또는 구문 분석 작업을 할 때 항상 고정 문화권이 사용됩니다

U

지역 표준시를 사용한 전체 날짜 및 시간(자세한 날짜와 자세한 시간)

y, Y

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl104',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.yearmonthpattern.aspx">YearMonthPattern

다음 표에서는 사용자 지정 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl105',this);" href="http://msdn.microsoft.com/ko-kr/library/system.datetime.aspx">DateTime 형식 패턴 및 해당 동작을 보여 줍니다. 자세한 내용은 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl106',this);" href="http://msdn.microsoft.com/ko-kr/library/8kb3ddd4.aspx">사용자 지정 날짜 및 시간 형식 문자열을 참조하십시오.

형식 패턴

설명

d, %d

날짜. 한 자리로 된 날짜 앞에는 0이 오지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%d"를 지정합니다.

dd

날짜. 한 자리로 된 날짜 앞에는 0이 옵니다.

ddd

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl107',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.abbreviateddaynames.aspx">AbbreviatedDayNames에 정의된 약식 요일 이름

dddd

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl108',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.daynames.aspx">DayNames에 정의된 자세한 요일 이름

f, %f

한 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%f"를 지정합니다.

ff

두 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다.

fff

세 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다.

ffff

네 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다.

fffff

다섯 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다.

ffffff

여섯 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다.

fffffff

일곱 자리 정밀도에서 초의 소수 부분입니다. 나머지 자릿수는 잘립니다.

F, %F

초의 소수 부분에 대한 최대 유효 자릿수를 표시합니다. 이 자릿수가 0이면 아무 것도 표시되지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%F"를 지정합니다.

FF

초의 소수 부분에 대한 최대 유효 자릿수를 2개로 표시합니다. 그러나 후행 0이나 2개의 0으로 된 숫자는 표시되지 않습니다.

FFF

초의 소수 부분에 대한 최대 유효 자릿수를 3개로 표시합니다. 그러나 후행 0이나 3개의 0으로 된 숫자는 표시되지 않습니다.

FFFF

초의 소수 부분에 대한 최대 유효 자릿수를 4개로 표시합니다. 그러나 후행 0이나 4개의 0으로 된 숫자는 표시되지 않습니다.

FFFFF

초의 소수 부분에 대한 최대 유효 자릿수를 5개로 표시합니다. 그러나 후행 0이나 5개의 0으로 된 숫자는 표시되지 않습니다.

FFFFFF

초의 소수 부분에 대한 최대 유효 자릿수를 6개로 표시합니다. 그러나 후행 0이나 6개의 0으로 된 숫자는 표시되지 않습니다.

FFFFFFF

초의 소수 부분에 대한 최대 유효 자릿수를 7개로 표시합니다. 그러나 후행 0이나 7개의 0으로 된 숫자는 표시되지 않습니다.

gg

서기. 형식이 지정될 날짜에 연관된 서기 문자열이 없는 경우 이 패턴은 무시됩니다.

h, %h

12시간제 시간. 한 자리로 된 시 앞에는 0이 오지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%h"를 지정합니다.

hh

12시간제 시간. 한 자리로 된 시 앞에는 0이 옵니다.

H, %H

24시간제 시간. 한 자리로 된 시 앞에는 0이 오지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%H"를 지정합니다.

HH

24시간제 시간. 한 자리로 된 시 앞에는 0이 옵니다.

K

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl109',this);" href="http://msdn.microsoft.com/ko-kr/library/system.datetime.kind.aspx">Kind 속성의 여러 값(로컬, UTC 또는 지정되지 않음)

m, %m

분. 한 자리로 된 분 앞에는 0이 오지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%m"을 지정합니다.

mm

분. 한 자리로 된 분 앞에는 0이 옵니다.

M, %M

숫자로 표현된 월. 한 자리로 된 달 앞에는 0이 오지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%M"을 지정합니다.

MM

숫자로 표현된 월. 한 자리로 된 달 앞에는 0이 옵니다.

MMM

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl110',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.abbreviatedmonthnames.aspx">AbbreviatedMonthNames에 정의된 약식 월 이름

MMMM

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl111',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.monthnames.aspx">MonthNames에 정의된 자세한 월 이름

s, %s

초. 한 자리로 된 초 앞에는 0이 오지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%s"를 지정합니다.

ss

초. 한 자리로 된 초 앞에는 0이 옵니다.

t, %t

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl112',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.amdesignator.aspx">AMDesignator 또는 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl113',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.pmdesignator.aspx">PMDesignator에 정의된 AM/PM 지정자의 첫 문자입니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%t"를 지정합니다.

tt

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl114',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.amdesignator.aspx">AMDesignator 또는 :Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl115',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.pmdesignator.aspx">PMDesignator에 정의된 AM/PM 지정자입니다. 응용 프로그램에서는 AM과 PM을 구분하는 데 필요한 해당 언어의 이 형식 패턴을 사용해야 합니다. 예를 들어 일본어 AM/PM 지정자의 경우 첫 번째 문자가 아니라 두 번째 문자가 서로 다릅니다.

y, %y

세기 표시 제외 연도. 세기 부분을 제외한 연도가 10보다 작은 경우 연도 앞에 0이 붙지 않습니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%y"를 지정합니다.

yy

세기 표시 제외 연도. 세기 부분을 제외한 연도가 10보다 작은 경우 연도 앞에 0이 붙습니다.

yyy

세 자리 연도. 연도가 100보다 작으면 연도 앞에 0이 붙습니다.

yyyy

세기를 포함한 네 자리 또는 다섯 자리 연도(사용하는 달력에 따라 다름). 네 자리 숫자를 만들기 위해 앞에 0을 채웁니다. 태국 불교식과 한국식 달력에서는 다섯 자리 연도를 사용합니다. 사용자가 "yyyy" 패턴을 선택하면 다섯 자리 숫자를 사용하는 달력에서 앞에 0이 없이 모두 숫자로만 구성된 다섯 자리가 표시됩니다. 예외: 일본식 달력과 대만식 달력에서는 항상 "yy"가 선택된 것처럼 동작합니다.

yyyyy

다섯 자리 연도. 다섯 자리 숫자를 만들기 위해 앞에 0을 채웁니다. 예외: 일본식 달력과 대만식 달력에서는 항상 "yy"가 선택된 것처럼 동작합니다.

yyyyyy

여섯 자리 연도. 여섯 자리 숫자를 만들기 위해 앞에 0을 채웁니다. 예외: 일본식 달력과 대만식 달력에서는 항상 "yy"가 선택된 것처럼 동작합니다. 앞에 0이 더 채워진 보다 긴 "y" 문자열을 사용하여 패턴이 계속될 수 있습니다.

z, %z

시간대 오프셋("+" 또는 "-" 뒤에 시간만 옴)입니다. 한 자리로 된 시 앞에는 0이 오지 않습니다. 예를 들어, 태평양 표준시는 "-8"입니다. 응용 프로그램에서는 형식 패턴이 다른 형식 패턴과 결합되지 않을 경우 "%z"를 지정합니다.

zz

시간대 오프셋("+" 또는 "-" 뒤에 시간만 옴)입니다. 한 자리로 된 시 앞에는 0이 옵니다. 예를 들어, 태평양 표준시는 "-08"입니다.

zzz

자세한 시간대 오프셋("+" 또는 "-" 뒤에 시간과 분이 옴)입니다. 한 자리로 된 시간과 분 앞에는 0이 옵니다. 예를 들어, 태평양 표준시는 "-08:00"입니다.

:

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl116',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.timeseparator.aspx">TimeSeparator에 정의된 기본 시간 구분 문자입니다.

/

:Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl117',this);" href="http://msdn.microsoft.com/ko-kr/library/system.globalization.datetimeformatinfo.dateseparator.aspx">DateSeparator에 정의된 기본 날짜 구분 문자입니다.

% c

여기서 c는 단독으로 사용된 경우의 형식 패턴 문자입니다. "d", "f", "F", "h", "m", "s", "t", "y", "z", "H" 또는 "M" 형식 패턴을 단독으로 사용하려면 응용 프로그램에서 "%d", "%f", "%F", "%h", "%m", "%s", "%t", "%y", "%z", "%H" 또는 "%M"을 지정합니다.

형식 패턴 문자가 리터럴 문자나 다른 형식 패턴 문자와 조합되어 사용될 경우 "%" 문자를 생략할 수 있습니다.

\ c

여기서 c는 임의의 문자입니다. 문자를 그대로 표시합니다. 백슬래시 문자를 표시하려면 "\\"를 사용해야 합니다.

위의 두 번째 표에 나열된 형식 패턴을 사용해야만 사용자 지정 패턴을 만들 수 있으며, 첫 번째 표에 나열된 표준 형식 패턴을 사용하면 표준 패턴만 만들 수 있습니다. 사용자 지정 패턴은 다음처럼 최소한 두 문자 길이입니다.

 

  • DateTime.ToString("d"):Track('ctl00_rs1_mainContentContainer_cpe283257_c|ctl00_rs1_mainContentContainer_ctl118',this);" href="http://msdn.microsoft.com/ko-kr/library/system.datetime.aspx">DateTime 값을 반환합니다. "d"는 간단한 표준 날짜 패턴입니다.

  • DateTime.ToString("%d")는 일을 반환합니다. "%d"는 사용자 지정 패턴입니다.

  • DateTime.ToString("d ")는 일과 그 다음에 공백 문자를 반환합니다. "d "는 사용자 지정 패턴입니다.

  •  

     

    다음 코드 예제에서는 en-US 문화권에 대해 서로 다른 형식 패턴을 출력합니다. 또한 해당 형식 패턴과 관련된 속성 값을 표시합니다.

     

     

     

    using System;
    using System.Globalization;


    public class SamplesDTFI  {

       public static void Main()  {

          // Creates and initializes a DateTimeFormatInfo associated with the en-US culture.
          DateTimeFormatInfo myDTFI = new CultureInfo( "en-US", false ).DateTimeFormat;

          // Creates a DateTime with the Gregorian date January 3, 2002 (year=2002, month=1, day=3).
          // The Gregorian calendar is the default calendar for the en-US culture.
          DateTime myDT = new DateTime( 2002, 1, 3 );

          // Displays the format pattern associated with each format character.
          Console.WriteLine( "FORMAT  en-US EXAMPLE" );
          Console.WriteLine( "CHAR    VALUE OF ASSOCIATED PROPERTY, IF ANY\n" );
          Console.WriteLine( "  d     {0}", myDT.ToString("d", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.ShortDatePattern, "(ShortDatePattern)" );
          Console.WriteLine( "  D     {0}", myDT.ToString("D", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.LongDatePattern, "(LongDatePattern)" );
          Console.WriteLine( "  f     {0}\n", myDT.ToString("f", myDTFI) );
          Console.WriteLine( "  F     {0}", myDT.ToString("F", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.FullDateTimePattern, "(FullDateTimePattern)" );
          Console.WriteLine( "  g     {0}\n", myDT.ToString("g", myDTFI) );
          Console.WriteLine( "  G     {0}\n", myDT.ToString("G", myDTFI) );
          Console.WriteLine( "  m     {0}", myDT.ToString("m", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.MonthDayPattern, "(MonthDayPattern)" );
          Console.WriteLine( "  M     {0}", myDT.ToString("M", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.MonthDayPattern, "(MonthDayPattern)" );
          Console.WriteLine( "  o     {0}\n", myDT.ToString("o", myDTFI) );
          Console.WriteLine( "  r     {0}", myDT.ToString("r", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.RFC1123Pattern, "(RFC1123Pattern)" );
          Console.WriteLine( "  R     {0}", myDT.ToString("R", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.RFC1123Pattern, "(RFC1123Pattern)" );
          Console.WriteLine( "  s     {0}", myDT.ToString("s", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.SortableDateTimePattern, "(SortableDateTimePattern)" );
          Console.WriteLine( "  t     {0}", myDT.ToString("t", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.ShortTimePattern, "(ShortTimePattern)" );
          Console.WriteLine( "  T     {0}", myDT.ToString("T", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.LongTimePattern, "(LongTimePattern)" );
          Console.WriteLine( "  u     {0}", myDT.ToString("u", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.UniversalSortableDateTimePattern, "(UniversalSortableDateTimePattern)" );
          Console.WriteLine( "  U     {0}\n", myDT.ToString("U", myDTFI) );
          Console.WriteLine( "  y     {0}", myDT.ToString("y", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.YearMonthPattern, "(YearMonthPattern)" );
          Console.WriteLine( "  Y     {0}", myDT.ToString("Y", myDTFI) );
          Console.WriteLine( "        {0} {1}\n", myDTFI.YearMonthPattern, "(YearMonthPattern)" );

       }

    }

    /*
    This code produces the following output.

    FORMAT  en-US EXAMPLE
    CHAR    VALUE OF ASSOCIATED PROPERTY, IF ANY

      d     1/3/2002
            M/d/yyyy (ShortDatePattern)

      D     Thursday, January 03, 2002
            dddd, MMMM dd, yyyy (LongDatePattern)

      f     Thursday, January 03, 2002 12:00 AM

      F     Thursday, January 03, 2002 12:00:00 AM
            dddd, MMMM dd, yyyy h:mm:ss tt (FullDateTimePattern)

      g     1/3/2002 12:00 AM

      G     1/3/2002 12:00:00 AM

      m     January 03
            MMMM dd (MonthDayPattern)

      M     January 03
            MMMM dd (MonthDayPattern)

      o     2002-01-03T00:00:00.0000000

      r     Thu, 03 Jan 2002 00:00:00 GMT
            ddd, dd MMM yyyy HH':'mm':'ss 'GMT' (RFC1123Pattern)

      R     Thu, 03 Jan 2002 00:00:00 GMT
            ddd, dd MMM yyyy HH':'mm':'ss 'GMT' (RFC1123Pattern)

      s     2002-01-03T00:00:00
            yyyy'-'MM'-'dd'T'HH':'mm':'ss (SortableDateTimePattern)

      t     12:00 AM
            h:mm tt (ShortTimePattern)

      T     12:00:00 AM
            h:mm:ss tt (LongTimePattern)

      u     2002-01-03 00:00:00Z
            yyyy'-'MM'-'dd HH':'mm':'ss'Z' (UniversalSortableDateTimePattern)

      U     Thursday, January 03, 2002 8:00:00 AM

      y     January, 2002
            MMMM, yyyy (YearMonthPattern)

      Y     January, 2002
            MMMM, yyyy (YearMonthPattern)

    */



    출처 : MSDN
    Posted by SB패밀리