You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
var builder = WebApplication.CreateBuilder(args);
-> 6.0버전부터 사용 가능
-> WebApplication.CreateBuilder는 미리 구성된 기본값을 사용해 클래스의 새 인스턴스를 초기화
-> 초기화 된 builder는 가장 높은 우선 순위 부터 가장 낮은 우선 순위까지 다음 순서로 앱의 기본 구성을 제공
명령줄 구성 공급자를 사용하는 명령줄 인수
접두사 없는 환경 변수 구성 공급자를 사용하는 접두사 없는 환경 변수
사용자 비밀 - 앱이 환경에서 실행되는 경우
JSON 구성 공급자를 사용하는 appsettings.{Environment}.json. 예를 들어 appsettings.Production.json 및 appsettings.Development.json를 지정
사용하지 않는 클래스, 함수, 필드를 실행되지 않도록 함
-> 코드를 없애기 보다는 남겨놓고 쓰지 않도록 하기 위함
-> 코드를 그냥 없앰으로써 생기는 문제를 방지
사용법
[Obsolete]// 아래의 클래스, 메소드, 필드가 더이상 사용되지 않는다는 경고 메시지가 뜸// 경고만 뜨고 실행은 정상적[Obsolete("New Method를 사용하시오")]// 위와 똑같이 작동// 인자로 들어간 string이 경고 메시지에 부가 정보로 뜸[Obsolete("New Method를 사용하시오",true)]// 두번째 인자로 true를 넣으면// 경고만 하는 것이 아닌 컴파일 에러를 내면서 실행 자체를 막음
Dll Import
관리되지 않는(.net 환경 밖에서 개발된 코드)DLL을 사용할 수 있게 함
사용법
using System;using System.Runtime.InteropServices;classDLLIport{[DllImport("User32.Dll")]// 인자로 사용할 DLL파일을 넣는다publicstaticexternintMessageBox(inth,stringm,stringc,inttype);// User32.Dll에 있는 MessageBox함수를 사용하는 것이기 때문에// extern키워드로 외부 메소드임을 알려준다staticvoidMain(){
MessageBox(0,"Hello!","In C#",0);}}
사용자 정의 Attribute
System.Attribute 클래스를 상속받아 만든 클래스로 사용자가 임의대로 새로운 어트리뷰트를 만들 수 있음
사용자 정의 Attribute는 한번 밖에 사용할 수 없다
-> System.AttributeUsage Attribute 선언부에 사용하면 여러번 사용 가능
사용법
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=true)]// 아래 attribute를 여러번 사용할 수 있게 함publicclassAuthor: System.Attribute // System.Attribute 상속하여 새로운 attribute 생성{privatestringName;publicdoubleVersion;publicAuthor(stringname)// Attribute의 기능 구현{Name=name;Version=1.0;}}[Author("P. Ackerman", Version =1.1)]// 다른 attribute 처럼 사용하면 됨classSampleClass{// P. Ackerman's code goes here...}
ApiController
형식 및 모든 파생 형식이 HTTP API응답을 제공하는데 사용됨을 나타냄
이 attribute가 선언된 부분은 API를 빌드하기 위해 개발자 환경을 개선하는 기능/동작으로 구성
ProblemDetails형식은 HTTP 응답에 머신에서 읽을 수 있는 오류 세부 정보를 제공하기 위해 RFC 7807 사양을 기반으로 함
사용법
[ApiController][Route("[controller]")]// ApiController를 사용하기 위해 선언 해야함publicclass WeatherForecastController : ControllerBase
Route
URL에 대한 경로를 정의 하기 위한 attribute
웹 API의 URL을 세세하게 제어할 수 있음
리소스의 계층 구조를 설명하는 URI를 쉽게 만들 수 있음
사용법
[Route("customers/{customerId}/orders")]// 변수로 URL의 경로를 정의publicIEnumerable<Order>GetOrdersByCustomer(intcustomerId){ ...}
HTTP
GET, POST 등의 작업을 하는 메서드로 만들어 주는 역할
종류
[HttpDelete]
[HttpGet]
[HttpHead]
[HttpOptions]
[HttpPatch]
[HttpPost]
[HttpPut]
Require
데이터 필드에 데이터가 반드시 입력되어야 한다는 것을 나타내는 attribute
문자열 크기 제한을 두기 위해 MinLength/StringLength attribute와 함께 사용되기도 함
RegularExpression attribute를 사용하여 데이터의 값이 지정된 규칙과 일치하는지 검사함
사용법
publicclassMovie{publicintId{get;set;}[Required]// 아래 데이터는 반드시 입력되어야 함[StringLength(100, MininumLength =1, ErrorMessage ="Title should be between 1 and 100 characters")]publicstringTitle{get;set;}=null!;[Required][StringLength(1000, MininumLength =100, ErrorMessage ="Description should be between 100 and 1000 characters")]// Title의 길이는 1 ~ 100이어야 하며 잘못된 입력 시 ErrorMessage가 나옴publicstringDescription{get;set;}=null!;}public IActionResult VerifyPhone([RegularExpression(@"^\d{3}-\d{3}-\d{4}$")]string phone)// RegularExpression attribute를 사용하여 전화번호 규칙과 일치하는지 검사{if(!ModelState.IsValid){return Json($"Phone {phone} has an invalid format. Format: ###-###-####");}return Json(true);}
객체지향 모델에서 기존 관계형 데이터베이스에 매핑하기 위한 프레임워크를 제공
-> 기존 데이터 타입을 객체지향 모델에서 이해할 수 있는 데이터로 변환해줌
장점
간단하고 속도가 빠름
SQL문을 입력하지 않고 파라미터와 쿼리만 입력하면 됨
쿼리 결과를 buffer에 담아서 반환하기 때문에 DB에 락을 적게 발생시킨다
패키지가 지원해주는 모든 DBMS를 연결할 수 있음
단점
쿼리를 직접 짜야하며 도메인 구조가 바뀌면 쿼리도 바꿔야함
복잡한 매핑은 지원하지 않음
Join해서 오브젝트 내부에 리스트를 넣는 등의 작업은 매핑 메소드를 따로 만들어야 함
쿼리
입력 데이터 매개 변수화
입력데이터를 그대로 쿼리에 쓸 경우 SQL Injection 공격에 취약함
SQL Injection: 악의적인 사용자가 임의의 SQL구문을 주입하여 의도하지 않은 명령을 수행하게 하는 것
그래서 데이터 그대로 쿼리문을 쓰는 것보다 객체나 class로 매핑하여 사용
-> 데이터가 노출되지 않음
// 1userInfo=await connection.QuerySingleOrDefaultAsync<DBUserInfo>(
sql:"select Email, HashedPassword, SaltValue from account where Email = @Email;", param:new{ Email = email}// 객체를 생성하여 인자로 넘겨주는 방식);//////////////////////////////////////////////////////////////////////////////////// 2// DBUserInfo라는 쿼리로 받을 정보를 저장할 class를 생성후DBUserInfouserInfo=new DBUserInfo();
userInfo.Email = request.Email;
userInfo.HashedPassword =hashingPassword;
userInfo.SaltValue =saltvalue;// 생성한 class를 넘겨서 데이터를 받는 방식var affectedRow:int=await connection.ExecuteAsync(sql:"INSERT INTO Account(Email, SaltValue, HashedPassword)
Values(@Email, @SaltValue, @HashedPassword);", userInfo);
Execute / ExecuteAsync
SQL명령을 실행시키는 메서드
인자로 SQL문과 필요에 따라 매개변수를 넣어준다
Execute / ExecuteAsync 메서드는 해당 SQL문을 실행하고 그 작업에 영향을 받은 행의 갯수를 리턴
stringsql="INSERT INTO Customers (Name, Email) VALUES (@Name, @Email);"object[]parameters={new{ Name ="John Doe", Email ="jdoe@example.com"}};using(varconnection=new SqlConnection(connectionString)){// 쿼리문과 Insert할 정보가 들어있는 객체를 넣어서 명령 실행
conn.Execute(sql, parameters);}
ExecuteScalar / ExecuteScalarAsync
SQL명령을 실행한 후의 결과의 첫번째 행의 첫번째 열의 값을 리턴하는 메서드
보통 Scalar함수의 결과를 가져오는 경우 사용(count, datetime, sum, avg 등...)
반환값이 객체임
단일 행 선택 쿼리
여러 개의 행 중 첫 번째의 행만 반환하는 메소드
종류
querySingle: 단일 행의 결과를 리턴
querySingleOrDefault: 단일 행의 결과를 리턴하거나 없는 경우 null리턴
queryFirst: 하나 이상의 행 중 첫 번째 행을 리턴
queryFirstOrDefault: 하나 이상의 행 중 첫 번째 행을 리턴하거나 없는 경우 null리턴
각 메소드는 querySingle형식으로 실행 결과를 특정 자료형이나 객체로 매핑할 수 있다
(반환 값은 T type으로 반환 됨)
여러 행 선택 쿼리
여러 행의 데이터를 가져올 수 있는 메소드
종류
Query
QueryAsync: 결과들을 비동기적으로 리턴
마찬가지로 Query로 사용하여 결과를 매핑하여 사용 가능
SQL Injection
악의적인 사용자가 보안상의 취약점을 이용하여 임의의 SQL문을 주입하고 실행시켜서 데이터베이스가 비정상적인 작업을 하도록 조작하는 행위
발생 조건
어플리케이션이 DB와 연동되어 있음
사용자가 입력한 외부 입력값이 SQL구문의 일부로 사용됨
예시
의도적인 주석 삽입
사용자가 admin이라는 ID를 사용하고 있는 것을 알고 있다고 가정했을 때 로그인 화면에서 아래처럼 입력함
ID: admin'--
password: 아무거나
위 입력값을 받아 아래 쿼리를 동적으로 생성함
SELECT* FROM USER WHERE ID = 'admin'--' AND Password ='아무거나'
위 쿼리에서 입력한 ID에서 WHERE절의 ID 조건을 강제로 닫아버리고 이하의 내용을 주석처리함
-> admin이 아이디인 계정에 비밀번호 검증을 하지 않고 바로 로그인을 시켜버리게됨
직접 데이터 베이스 조작
게시글을 올릴 때 아래 쿼리가 생성되어 실행하는 게시판이 있다고 가정
INSERT INTO BOARD(id,title,contents) VALUE('user', '제목', '내용')
사용자가 게시글을 작성할 때 아래의 내용을 입력
제목: SI
내용: '); DELETE FROM BOARD--
위 입력값으로 인해 아래 쿼리가 동적으로 생성됨
INSERT INTO BOARD(id,title,contents) VALUE('user', 'SI', '');DELETEFROM BOARD--')
위 쿼리가 실행 되면 내용이 비어있는 게시글이 추가되고 BOARD테이블의 모든 데이터를 삭제함
해결 방안
Dapper에서는 매개변수화 된 쿼리를 만들어서 SQL Injection 공격에 대응할 수 있음
쿼리에 필요한 데이터들이 직접적으로 들어가지 않고 자리 표시자(이름 앞에 @문자가 붙은 매개변수)를 통해 전달이 됨
자리 표시자를 통해 전달 된 데이터는 문자열 치환이 아니라 별도의 명령문으로 전달되어 주입된 쿼리를 실행할 수 없게 함
나누어지지 않는 최소한의 단위로 만들어서 all or nothing 전략을 취할 수 있도록 함
즉 어떤 명령이 실패를 하게 되면 명령들을 모두 취소시키고 아니면 모두 실행함
Redis는 싱글스레드 기반이기 때문에 동시성 문제를 처리해야함 -> 트랜잭션으로 처리
Redis에서 트랜잭션을 유지하려면 순차성을 가져야하고 도중에 명령어가 들어오지 못하게 Lock을 걸어야함
명령들을 Queue에 쌓아두고 한번에 처리함
MULTI 명령어 이후 잘못된 명령어를 사용 시 자동으로 DISCARD실행
단 잘못된 자료구조의 명령어일 경우 DISCARD되지 않고 그대로 실행
-> 대부분 개발 과정에서 일어날 수 있는 에러이기 때문
명령어
MULTI
트랜잭션을 준비하는 명령어. 이 명령어 이후 명령은 바로 실행되지 않고 queue에 저장
EXEC
트랜잭션을 시작하는 명령어. MULTI이후 Queue에 쌓인 명령들을 차례대로 실행시킴
DISCARD
Queue에 쌓인 명령들을 지워버림
WATCH, UNWATCH
Lock을 담당하는 명령어
이 명령어를 사용한 이후 UNWATCH가 되기전엔 한번의 EXEC또는 트랜잭션이 아닌 명령어만 허용
MULTI, EXEC만으로는 동시성 문제의 트랙잭션의 고립성을 보장할 수 없으므로 사용
WATCH로 인해 예외 발생 시 DISCARD를 사용하여 문제 처리
EXEC가 호출되면 묵시적으로 UNWATCH가 호출됨
Pub Sub
특정한 주제에 대해 해당 주제를 구독한 모두에게 메시지를 발행하는 통신 방법
하나의 client가 메세지를 pub하면 이 주제에 연결되어있는 다수의 client가 메시지를 받음
(youtube의 채널 구독과 비슷함. 채널에서 새로운 영상이 올라오면 구독자한테 알림이 가는 것)
이 시스템은 매우 단순한 구조로 되어있음
메시지를 던지기만 하고 따로 보관하지 않음
-> 수신자가 메시지를 받는 것을 보장하지 않고 일반 메시지queue처럼 수신 확인을 하지 않음
Redis는 인메모리기반이라 매우 빠르게 메시지를 보낼 수가 있음
명령어
SUBSCRIBE
해당 채널을 구독하여 메시지를 수신받도록 대기
(EX) SUBSCRIBE channel
PSUBSCRIBE
나열한 패턴에 일치하는 채널을 구독
(EX) PSUBSCRIBE pattern
PUBLISH
메시지를 해당 채널로 송신. 해당 채널에서 대기중인 클라이언트들에게 메시지가 전달됨
(EX) PUBLISH channel message
UNSUBSCRIBE
해당 채널의 구독 취소
(EX) UNSUBSCRIBE channel1 channel2
PUNSUBSCRIBE
해당 패턴에 일치하는 채널의 구독 취소
(EX) PUNSUBSCRIBE pattern1 pattern2
PUBSUB
서버에 등록된 채널이나 패턴 조회
ZLogger
Microsoft.Extensions.Logging 위에 구축된 .NET Core 및 Unity용 제로 할당 텍스트/구조적 로거
특징
ZString에 의해 UTF8로 직접 버퍼영역에 쓰게되고 ConsoleStream에 정리해서 보내주기 때문에 Boxing이 일어나지 않고 비동기적으로 단번에 쓰기 때문에 부하를 주지 않음
Boxing: value형식을 reference(object)형식으로 변환해주는 것
철저한 제네릭, 구조체, 캐시를 활용하여 최대의 성능을 구현
ConsoleLogging외에도 FileLogger, RollingFileLogger, StreamLogger를 제공
ZString
.NET Core 및 Unity용 메모리 할당을 0으로 설정하는 string 빌더
사용이유
대부분의 경우에 기존의 String보다 메모리 할당량과 속도가 적음
사용법
using ZLogger;// namespace
Host.CreateDefaultBuilder().ConfigureLogging(logging =>{// 기본 제공자 정의 logging.ClearProviders();// optional(MS.E.Logging): default값은 Info이며 option을 바꿔 최소 로그레벨을 지정해 줄 수 있다 logging.SetMinimumLevel(LogLevel.Debug);// 콘솔에 로그 출력 logging.AddZLoggerConsole();// 파일에 로그 출력 logging.AddZLoggerFile("fileName.log");// 날짜/시간 또는 파일 크기에 따라 출력 파일 경로가 변경된 파일로 출력 logging.AddZLoggerRollingFile((dt,x)=>$"logs/{dt.ToLocalTime():yyyy-MM-dd}_{x:000}.log",x => x.ToLocalTime().Date,1024);// 구조화된 로깅 사용 logging.AddZLoggerConsole(options =>{ options.EnableStructuredLogging =true;});})
로그 출력
AddZLoggerConsole
콘솔에 로그를 출력함
ConsoleApplication 및 클라우드의 컨테이너화된 Application에 유용
AddZLoggerFile
string 인자로 넣은 파일로 로그를 출력함
RollingFile
날짜/시간 또는 파일 크기에 따라 출력 파일 경로가 변경된 파일로 출력
logging.AddZLoggerRollingFile(// Func<DateTimeOffset, int, string> fileNameSelector// 생성된 로그파일의 경로 선택기
fileNameSelector:(dt,x)=>$"logs/{dt.ToLocalTime():yyyy-MM-dd}_{x:000}.log",// Func<DateTimeOffset, DateTimeOffset> timestampPattern// 로그가 작성되는 현재시간을 인수로 받으며// 반환 값이 마지막으로 작성된 시간과 다른 경우 // fileNameSelector를 호출하여 새파일에 작성
timestampPattern:x => x.ToLocalTime().Date,// int rollSizeKB// 파일크기 제한// 크기가 넘어가면 fileNameSelector를 호출하여 새파일에 작성
rollSizeKB:1024);
구조화된 로깅
클라우드(Amazon Ahtena, Google BigQuery, Azure Data Lake.. 등) 로깅을 하는데 있어 중요한 역할
System.Text.Json.JsonSerializer를 사용하여 문자열로 변환하지 않고 파이프라인에서 완전한 제로 할당이 가능
JSON으로 로그를 작성하고 추가 정보(카테고리 이름, loglevel, 시간, 사용자 정의 메타데이터) + 메시지, 정보 + 메시지 + 사용자 정의 페이로드를 지원