>네이버크롤링 APT정보를 이용한 메인지번,부번 구하기(for JOIN)
>전국을 대상으로 조회시에 반복문이 많아지므로 지역단위로 쪼개서 쓰레드호출


-rwxrwxrwx 1 sinfo sinfo 24993792 Feb  1 10:40 naver_estate_info_file.db +
-rwxrwxrwx 1 sinfo sinfo  4341760 Feb  5 20:09 address_estate_info_file.db = 
-rwxrwxrwx 1 sinfo sinfo 23379968 Feb  5 20:19 naver_attach_estate_info_file.db
if (sender is Button button)
{
button.BackColor = Color.DarkGreen;

CHKNM nmm = new CHKNM();
await Task.Run(() => nmm.RUN(button.Text, listBoxTitleNM, listBoxCountNM, listBoxErrCountNM));

button.Enabled = false;
button.BackColor = Color.LightGray;
}

 

 

"지역별, 부동산 매매가격에 대한 최고매매와 최소매매의 가격 차이 통계"를 영어로 하면?
"Regional Real Estate Sales Price Difference Statistics: Maximum vs. Minimum Sale Prices"

1. 국토교통부 실거래가 조회.사이트에서 2019-2025년 매매자료 다운로드후에 SQLite에 입력
- 엑셀다운로드후에 편집후 자동업로드
2. 전국 네이버 아파트.기본 정보 크롤링을 통한 SQLite에 입력
- 자동크롤링
3. 1과 2를 조인해서 웹페이지 보여주는데, 공통조인 대상이 부족함
4. business.juso.go.kr 을 통해서, 네이버아파트정보의 지역명+아파트명을 조합해서 지번을 구함(본번,부번)
- 자동크롤링
5. 1과 2와 4를 조인해서 웹페이지 보여주는데, 이빨이 빠진부분(회색)이 있음
6. 이빨이 빠진부분(회색)이 있는 이유는 네이버아파트정보의 지역명+아파트명으로도 지번을 못구함(본번,부번)

>"Now Loading......" 추가
>"Now Loading......" 추가
>"Now Loading......" 추가

<style>
#loading {
display: none; /* 기본적으로 숨김 */
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 20px;
border-radius: 10px;
font-size: 20px;
}
</style>

 

1) 02-02  오전 10:00       144,285,696 database_all_total.db - 국토교통부 실거래가 자료
2) 02-02  오전 10:44           196,608 descending_in_base_all.db - 매매최대,최소갭 상위 200개 지역별 저장자료
3) 02-05  오후 08:19        23,379,968 naver_attach_estate_info_file.db - 지번이 포함되어진 네이버아파트 자료
4) 02-01  오전 10:40        24,993,792 naver_estate_info_file.db - 지번이 없는 네이버아파트 자료

1) 
dotnet new console -o RealEstateFileInsert
2)
dotnet new console -o RealEstateDescendingFileInsert
4)
dotnet new console -o RealNaverAutoCrollingDataInsert
3)
dotnet new winforms -o RealAddressAttachAutoCrollingDataInsert

index.html
0.02MB

 

C#에서 메소드 내의 지역 변수(local variable) 는 반드시 초기화를 해야 합니다. 그 이유는 다음과 같습니다.


1. 지역 변수는 자동으로 초기화되지 않음

C#에서 지역 변수는 자동으로 초기화되지 않습니다. 따라서 초기화하지 않은 상태에서 사용하면 컴파일 오류가 발생합니다.

✅ 예제 (초기화하지 않고 사용하면 오류 발생)

void TestMethod()
{
    int number; // 초기화하지 않음
    Console.WriteLine(number); // 오류! 초기화되지 않은 변수 사용
}

🚨 오류 메시지

 
Use of unassigned local variable 'number'

💡 반면, 필드(Field)는 자동으로 초기화됨
클래스의 필드(Field)는 자동으로 기본값(default value)으로 초기화되므로 초기화를 하지 않아도 오류가 발생하지 않습니다.

class TestClass
{
    int number; // 필드는 자동으로 0으로 초기화됨

    void PrintNumber()
    {
        Console.WriteLine(number); // 정상 작동 (출력: 0)
    }
}

2. 메모리 안전성 보장

C#이 지역 변수를 초기화 없이 사용하지 못하도록 막는 이유는 메모리 안전성을 보장하기 위해서입니다.
C, C++에서는 초기화되지 않은 변수를 사용할 경우 메모리에 남아 있는 쓰레기 값(Garbage Value) 을 가져와 예상치 못한 동작이 발생할 수 있습니다.
C#에서는 이러한 문제를 방지하고 안정적인 실행을 보장하기 위해 지역 변수를 명시적으로 초기화하도록 강제합니다.


3. 명확한 코드 유지

초기화를 강제함으로써 코드의 명확성과 가독성이 향상됩니다.
만약 초기화를 하지 않아도 사용 가능하다면, 프로그래머가 실수로 쓰레기 값을 참조할 가능성이 높아지고, 디버깅이 어려워질 수 있습니다.


4. 예외: out 키워드 사용 시 초기화 필요 없음

일반적인 지역 변수는 반드시 초기화해야 하지만, out 키워드를 사용하면 초기화 없이 메소드에 전달 가능합니다.

void SetValue(out int x)
{
    x = 10; // 반드시 값 할당해야 함
}

void Test()
{
    int number; // 초기화하지 않음
    SetValue(out number); // out 매개변수는 초기화 필요 없음
    Console.WriteLine(number); // 정상 출력 (10)
}

💡 out 키워드는 메소드 내부에서 반드시 값을 설정하도록 보장하므로, 지역 변수를 초기화할 필요가 없습니다.


결론

C#에서 메소드 내의 지역 변수를 초기화해야 하는 이유는?

  1. 자동 초기화되지 않기 때문 → 초기화 없이 사용하면 컴파일 오류 발생
  2. 메모리 안전성을 보장하기 위해 → C, C++의 쓰레기 값 문제 방지
  3. 코드 가독성과 유지보수성을 향상시키기 위해
  4. 예외적으로 out 매개변수는 초기화 없이 사용 가능

✅ 해결 방법:
지역 변수를 사용하기 전에 반드시 초기화하여 오류를 방지하세요! 

국토교통부 실거래가자료 사이트기반
1) 조회년월 - 2019.01-2025.01

2) 조회대상 - 경기도

3) 조회조건
3.1) 최대거래매매금액, 일자, 층수
3.2) 최소거래매매금액, 일자, 층수
3.3) 중개거래만 대상(직거래 제외)

4)결과
4.1) 최대거래매매금액 - 최소거래매매금액
4.2) (최대거래매매금액 - 최소거래매매금액)/(최대거래매매금액)*100.0
4.3) 4.2의 결과중에서, 최대거래매매금액이 큰 순서대로


 

RESPONSE-CHAT.GPT/DEEP.SEEK
RESPONSE-CHAT.GPT/DEEP.SEEK

REQUEST)
거래소 주식 TCP 데이타는 회원사들이 100% 일방적으로 수신만 합니다. 거래소는 일방적으로 송신만 합니다. 그럼 회원사들이 사용하는 수신TCP프로그램을 작성하고 싶습니다. 데이타의 구분자는 3개의 딜리미터로 구분됩니다. 0x0a,0a0d,0xff입니다. c#으로 회원사 수신프로그램을 작성해주세요

RESPONSE-CHAT.GPT)

using System;
using System.Net;
u

sing System.Net.Sockets;

using System.Text;
using System.Threading;

class TcpServer
{
    private const int Port = 12345; // 서버 포트
    private TcpListener tcpListener;
    private bool isRunning;

    public TcpServer()
    {
        tcpListener = new TcpListener(IPAddress.Any, Port);
        isRunning = true;
    }

    public void Start()
    {
        tcpListener.Start();
        Console.WriteLine("TCP 서버가 시작되었습니다.");

        while (isRunning)
        {
            try
            {
                // 클라이언트 연결 대기
                var tcpClient = tcpListener.AcceptTcpClient();
                Console.WriteLine("클라이언트 연결됨.");

                // 클라이언트에서 데이터를 수신하는 작업 수행
                Thread clientThread = new Thread(() => HandleClient(tcpClient));
                clientThread.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"오류: {ex.Message}");
            }
        }
    }

    private void HandleClient(TcpClient tcpClient)
    {
        NetworkStream stream = tcpClient.GetStream();
        byte[] buffer = new byte[1024];
        StringBuilder sb = new StringBuilder();

        while (true)
        {
            try
            {
                int bytesRead = stream.Read(buffer, 0, buffer.Length);
                if (bytesRead == 0)
                {
                    break; // 연결 종료
                }

                // 받은 데이터를 문자열로 변환
                sb.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));

                // 딜리미터에 맞춰 데이터 분리
                string data = sb.ToString();
                string[] messages = SplitByDelimiters(data);

                foreach (string message in messages)
                {
                    if (!string.IsNullOrEmpty(message))
                    {
                        Console.WriteLine($"수신된 메시지: {message}");
                    }
                }

                // 남은 데이터를 다시 설정
                sb.Clear();
                sb.Append(data.Substring(data.LastIndexOf(messages[messages.Length - 1]) + messages[messages.Length - 1].Length));
            }
            catch (Exception ex)
            {
                Console.WriteLine($"오류: {ex.Message}");
                break;
            }
        }

        tcpClient.Close();
        Console.WriteLine("클라이언트 연결 종료.");
    }

    private string[] SplitByDelimiters(string input)
    {
        // 3개의 딜리미터에 따라 데이터를 분리합니다.
        var delimiters = new string[] { "\x0A", "\x0D\x0A", "\xFF" };
        var messages = input;

        // 딜리미터를 기준으로 데이터를 분리
        foreach (var delimiter in delimiters)
        {
            messages = messages.Replace(delimiter, "\n"); // 구분자를 줄바꿈 문자로 바꿔서 Split
        }

        return messages.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
    }

    public void Stop()
    {
        isRunning = false;
        tcpListener.Stop();
    }

    static void Main(string[] args)
    {
        TcpServer server = new TcpServer();
        server.Start();
    }
}

 


REQUEST)
거래소 주식 TCP 데이타는 회원사들이 100% 일방적으로 수신만 합니다. 거래소는 일방적으로 송신만 합니다. 그럼 회원사들이 사용하는 수신TCP프로그램을 작성하고 싶습니다. 데이타의 구분자는 3개의 딜리미터로 구분됩니다. 0x0a,0a0d,0xff입니다. c#으로 회원사 수신프로그램을 작성해주세요

RESPONSE-DEEP.SEEK)

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        string serverIP = "거래소서버IP"; // 거래소 서버 IP 주소
        int port = 12345; // 거래소 서버 포트 번호

        TcpClient client = new TcpClient(serverIP, port);
        NetworkStream stream = client.GetStream();

        byte[] buffer = new byte[1024];
        StringBuilder dataReceived = new StringBuilder();

        while (true)
        {
            int bytesRead = stream.Read(buffer, 0, buffer.Length);
            if (bytesRead == 0)
            {
                // 서버와의 연결이 끊어졌을 경우
                break;
            }

            string chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            dataReceived.Append(chunk);

            ProcessReceivedData(dataReceived);
        }

        stream.Close();
        client.Close();
    }

    static void ProcessReceivedData(StringBuilder dataReceived)
    {
        string data = dataReceived.ToString();
        string[] delimiters = { "\x0a", "\x0d", "\xff" };

        // 데이터를 구분자로 분리
        string[] messages = data.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);

        foreach (string message in messages)
        {
            Console.WriteLine("Received: " + message);
        }

        // 처리된 데이터는 제거
        dataReceived.Clear();
    }
}

 

 

2020.01 - 2025.01 까지 국토교통부 실거래자료 입력후에 분석해본 통계결과
기자료)


1. 2020-2025 경기도 안양시 동안구 매매자료 download후에, SQLite에 입력후 조회
2. 2020-2025 서울시 마포구 매매자료 download후에, SQLite에 입력 후 조회
3. 2019-2025 서울시 전체 매매자료 download후에, SQLite에 입력 후 조회



프로그램화)
1. 입력부
2. 조회부

분석결과)

서울은 마포구와 강남구를 조사했는데, 모두 신고가가 최근에 이루어지고 있는것을 확인할수 있었고,
나머지 지역은 2021-2022 저금리시기, 급등기에 이루어진 거래였다는 점입니다.
최근은 똘똘한 한채로 신고가 매매가 이루어 지는것 같고, 그 지역은 서울과 수도권의 알짜단지인것 같습니다.

 

아파트실거래가_통계_매매_MAX_MIN_지역별(20250131).xlsx
0.03MB

윈도우 10을 설치한 후에 윈도우 11로 자동 업그레이드되지 않도록 설정하려면 다음 단계를 따르세요:


1. 윈도우 업데이트 일시 중지

윈도우 업데이트를 일시 중지하면, 일정 기간 동안 자동 업그레이드가 진행되지 않습니다.

  • 방법:
    1. 설정 > 업데이트 및 보안 > Windows 업데이트로 이동.
    2. "업데이트 일시 중지" 옵션을 클릭하고 최대 35일까지 중지.

2. 업그레이드 차단을 위한 레지스트리 수정

윈도우 11로의 업그레이드를 영구적으로 차단하려면 레지스트리를 수정해야 합니다. 주의: 레지스트리를 잘못 수정하면 시스템에 문제가 생길 수 있으니 주의하세요.

  • 방법:
    1. Win + R 키를 눌러 regedit 입력 후 실행.
    2. 다음 경로로 이동:
      복사편집
      HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate
    3. 오른쪽 빈 공간에서 마우스 오른쪽 버튼 클릭 > 새로 만들기 > DWORD (32비트) 값 선택.
    4. 새 값을 TargetReleaseVersion으로 이름 지정.
    5. 더블 클릭하고 값 데이터를 1로 설정.
    6. 같은 경로에서 또 다른 키를 생성:
      • 이름: TargetReleaseVersionInfo
      • 값 데이터: 21H2 (윈도우 10의 마지막 안정 버전)

3. 그룹 정책 설정 (Pro/Enterprise 에디션 전용)

윈도우 10 Pro 이상 사용자라면 그룹 정책 편집기를 사용하여 업그레이드를 차단할 수 있습니다.

  • 방법:
    1. Win + R 키를 누르고 gpedit.msc 입력 후 실행.
    2. 다음 경로로 이동:
      rust
      복사편집
      컴퓨터 구성 > 관리 템플릿 > Windows 구성 요소 > Windows 업데이트 > Windows 업데이트 for Business
    3. 대상 기능 업데이트 버전 설정 더블 클릭.
    4. "사용"을 선택한 후, 버전을 21H2로 입력.
    5. 적용 후 확인.

4. 업데이트 도우미 비활성화

마이크로소프트의 "Windows 11 설치 도우미"가 설치되어 있다면 이를 제거하세요.

  • 방법:
    1. 제어판 > 프로그램 및 기능으로 이동.
    2. 설치된 목록에서 "Windows 11 설치 도우미"를 찾아 제거.

5. 타사 도구 사용

Shutup10++ 같은 타사 소프트웨어를 사용하면, 윈도우 업데이트 및 특정 기능 업그레이드를 간단히 차단할 수 있습니다.

  • 공식 사이트에서 다운로드하여 "윈도우 11 업그레이드 차단" 옵션을 활성화하세요.

'리눅스 > Windows' 카테고리의 다른 글

CPU와 GPU의 역할 차이  (0) 2024.11.07

Visual Studio에서 32비트 프로그램을 64비트로 마이그레이션하는 방법은 다음과 같습니다:

1. 프로젝트 설정 확인

  • 기존 프로젝트가 32비트로 설정된 경우, 이를 64비트로 변경해야 합니다. 이를 위해 프로젝트 파일을 열고, 빌드 설정을 64비트로 변경합니다.

2. 프로젝트 속성 변경

  • 프로젝트 우클릭 → 속성으로 이동합니다.
  • **구성 관리(Configuration Manager)**로 가서, 활성(Active) 설정을 64비트로 변경합니다.
  • 플랫폼(Platform) 옵션에서 x64를 선택합니다. 만약 x64 옵션이 없다면, 새로 만들기(Create New) 버튼을 클릭하여 x64를 추가해 줍니다.

3. 코드 변경

  • 포인터 크기: 64비트에서 포인터는 64비트이므로, 포인터 크기와 관련된 코드가 있다면 이를 64비트에 맞게 조정해야 할 수 있습니다.
  • 정수형 변수 크기: 32비트에서는 int나 long의 크기가 4바이트였으나, 64비트 시스템에서는 long의 크기가 8바이트일 수 있습니다. 이를 고려하여 코드 수정이 필요할 수 있습니다.
  • DLL과 라이브러리 호환성: 사용하는 외부 라이브러리나 DLL이 64비트 버전으로 제공되는지 확인하고, 필요하다면 64비트 버전으로 교체합니다.

4. 빌드 및 디버깅

  • 프로젝트가 64비트로 설정되었으면, 빌드(Build) 버튼을 눌러 64비트 애플리케이션으로 빌드합니다.
  • 빌드가 성공한 후, **디버깅(Debugging)**을 통해 64비트 환경에서 제대로 동작하는지 확인합니다.

5. 종속성 확인

  • 64비트 환경에서 사용하는 모든 외부 종속성(예: 외부 DLL, 라이브러리 등)이 64비트 버전과 호환되는지 확인합니다. 32비트 라이브러리와 64비트 애플리케이션은 호환되지 않습니다.

6. 테스트

  • 애플리케이션이 예상대로 64비트 환경에서 잘 동작하는지 충분히 테스트합니다. 특히 메모리 처리나 성능 관련 부분에서 차이가 있을 수 있으므로, 세밀하게 검토해야 합니다.

위 단계를 따르면 기존 32비트 프로젝트를 64비트로 마이그레이션할 수 있습니다. 이 과정에서 발생할 수 있는 호환성 문제를 처리하기 위해, 코드 수정이 필요할 수 있다는 점을 염두에 두세요.


다량 데이타 입력을 처리하는 배치프로그램(성능향상)

다량 데이타 입력을 처리하는 배치프로그램에서 아래의 사항을 고려하면, 보다 효과적으로 속도를 개선시킬서수 있습니다.


OCI 환경 초기화

배치 처리를 위한 바인드 변수 설정

 

Oracle Call Interface(OCI)를 통해 다량의 데이터를 입력할 때, 성능을 최적화하려면 다음과 같은 전략을 사용할 수 있습니다.

OCI를 사용하여 데이터베이스 연결을 초기화합니다.

OCI에서는 다중 바인드 변수와 배열 인터페이스를 사용하여 대량 데이터를 효율적으로 처리할 수 있습니다.예: 한 번에 1000건의 데이터를 삽입하는 코드

OCIEnv *env;
OCIError *err;
OCISvcCtx *svc;
OCIStmt *stmt;

OCIEnvCreate(&env, OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
OCIHandleAlloc(env, (void **)&err, OCI_HTYPE_ERROR, 0, NULL);
OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);

// 데이터베이스 연결
OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);
OCILogon(env, err, &svc, "username", strlen("username"), "password", strlen("password"), "dbname", strlen("dbname"));

 

#define BATCH_SIZE 1000
#define MAX_COLUMN_LENGTH 100

char col1[BATCH_SIZE][MAX_COLUMN_LENGTH];
int col2[BATCH_SIZE];

OCIStmt *stmt;
OCIHandleAlloc(env, (void **)&stmt, OCI_HTYPE_STMT, 0, NULL);

// SQL 준비
char *sql = "INSERT INTO my_table (column1, column2) VALUES (:1, :2)";
OCIStmtPrepare(stmt, err, (text *)sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);

// 바인드 변수 설정
OCIBind *bnd1 = NULL, *bnd2 = NULL;
OCIBindByPos(stmt, &bnd1, err, 1, (void *)col1, MAX_COLUMN_LENGTH, SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
OCIBindByPos(stmt, &bnd2, err, 2, (void *)col2, sizeof(int), SQLT_INT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);

// 데이터 채우기
for (int i = 0; i < BATCH_SIZE; i++) {
    snprintf(col1[i], MAX_COLUMN_LENGTH, "Value_%d", i);
    col2[i] = i;
}

// 데이터 배치 삽입
OCIBindArrayOfStruct(bnd1, err, MAX_COLUMN_LENGTH, 0, 0, 0);
OCIBindArrayOfStruct(bnd2, err, sizeof(int), 0, 0, 0);

OCIStmtExecute(svc, stmt, err, BATCH_SIZE, 0, NULL, NULL, OCI_DEFAULT);


부가적인 성능 최적화

  1. 네트워크 왕복 최소화:
    • 한 번에 최대한 많은 데이터를 삽입하여 네트워크 왕복 횟수를 줄입니다.
  2. PL/SQL 프로시저 활용:
    • PL/SQL 저장 프로시저를 작성하고, 데이터를 바인딩하여 호출하면 성능이 더욱 향상됩니다.
  3. 병렬 처리:
    • Oracle Parallel DML을 활성화하거나 데이터를 병렬로 분할하여 삽입합니다.
  4. COMMIT 빈도 조정:
    • 대량의 데이터를 삽입할 때 너무 자주 커밋하면 성능이 저하될 수 있으므로 적절한 빈도로 조정합니다.

Kafka 설치 방법

Kafka를 설치하고 설정하는 단계는 다음과 같습니다:


1. 사전 준비

  • Kafka를 실행하려면 Java(JDK 8 이상)와 ZooKeeper(또는 KRaft)를 설치해야 합니다.
  • Kafka 설치를 위한 사양:
    • CPU: 클러스터 환경에서는 여러 코어를 권장
    • RAM: 최소 8GB 이상 권장
    • 디스크: SSD 사용 권장 (빠른 I/O 속도를 위해)

2. Kafka 다운로드

  1. Apache Kafka 공식 사이트에서 최신 버전을 다운로드합니다.
  2. 압축을 해제합니다:
     
    tar -xzf kafka_<version>.tgz cd kafka_<version>

3. ZooKeeper 실행

Kafka는 기본적으로 ZooKeeper에 의존해 메타데이터를 관리합니다. (Kafka 2.8 이상부터 KRaft로 Zookeeper 없이 설정 가능)

 
bin/zookeeper-server-start.sh config/zookeeper.properties

4. Kafka 브로커 실행

Kafka 브로커를 시작합니다.

 
bin/kafka-server-start.sh config/server.properties

5. 토픽 생성

Kafka에서 데이터를 저장할 토픽을 생성합니다.

 
bin/kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --partitions 3 --replication-factor 1

6. 프로듀서 실행

Kafka에 메시지를 보내는 프로듀서를 실행합니다.

 
bin/kafka-console-producer.sh --topic test-topic --bootstrap-server localhost:9092

메시지를 입력하면 해당 메시지가 Kafka에 저장됩니다.

7. 컨슈머 실행

Kafka에서 메시지를 읽는 컨슈머를 실행합니다.

 
bin/kafka-console-consumer.sh --topic test-topic --from-beginning --bootstrap-server localhost:9092

Kafka 동작 원리

Kafka의 동작은 다음 구성 요소를 중심으로 이해할 수 있습니다:

1. 토픽과 파티션

  • Kafka는 데이터를 토픽이라는 논리적 채널에 저장합니다.
  • 각 토픽은 여러 파티션으로 나뉘어 데이터를 분산 저장합니다.
    • 파티션은 순차적으로 데이터를 저장하며, 메시지는 **오프셋(offset)**이라는 고유 번호를 가집니다.

2. 프로듀서

  • 프로듀서는 데이터를 Kafka 토픽으로 전송합니다.
  • 데이터를 어느 파티션에 저장할지 결정:
    • 기본적으로 라운드로빈 방식
    • 특정 키를 지정해 동일한 파티션에 저장 가능 (e.g., 고객 ID 기반)

3. 브로커

  • Kafka 클러스터의 각 노드는 브로커로 작동합니다.
  • 브로커는 데이터를 저장하고, 소비자가 요청할 때 데이터를 제공합니다.
  • 토픽의 각 파티션은 리더와 여러 팔로워로 구성되며, 리더가 데이터를 처리하고 팔로워가 복제합니다.

4. 컨슈머

  • 컨슈머는 Kafka에서 데이터를 읽어옵니다.
  • 컨슈머 그룹을 사용해 데이터 병렬 처리 가능:
    • 동일 그룹의 컨슈머는 동일한 파티션 데이터를 처리하지 않습니다.

5. 복제와 내구성

  • Kafka는 각 파티션의 데이터를 여러 브로커에 복제해 장애 복구를 지원합니다.
    • replication-factor를 설정해 복제본 수를 지정.
    • 한 브로커가 다운되더라도 다른 브로커가 데이터를 제공합니다.

Kafka 데이터 흐름 요약

  1. 프로듀서가 메시지를 생성하여 토픽으로 전송.
  2. 메시지는 브로커에 의해 저장되고, 파티션으로 분산.
  3. 컨슈머가 토픽에서 메시지를 읽어와 처리.

Kafka의 이러한 구조는 높은 확장성, 내구성, 실시간 처리를 제공합니다.


rcv_info_to_kafka.cs
0.00MB

'KAFKA' 카테고리의 다른 글

분산 이벤트 스트리밍 플랫폼(Kafka)  (0) 2025.01.21
APACHE KAFKA 데이타마이그래이션 작업(102)  (0) 2019.10.31

Kafka는 Apache Software Foundation에서 개발한 오픈소스 분산 이벤트 스트리밍 플랫폼으로, 대규모 데이터 처리를 위해 설계되었습니다. 주로 실시간 데이터 스트리밍, 로그 처리, 이벤트 기반 애플리케이션 등에 사용됩니다.

주요 특징

  1. 분산 시스템
    Kafka는 여러 서버로 구성된 클러스터로 동작하며, 데이터를 분산 처리하여 높은 가용성과 확장성을 제공합니다.
  2. 퍼블리셔-구독자 모델 (Pub/Sub)
    데이터는 생산자(Producer)가 주제를 통해 Kafka로 전송하고, 소비자(Consumer)가 해당 데이터를 구독하여 처리합니다.
  3. 높은 처리량
    초당 수백만 건의 메시지를 처리할 수 있으며, 실시간 대규모 데이터 처리에 적합합니다.
  4. 내구성 및 안정성
    데이터를 디스크에 저장하고, 복제를 통해 데이터 손실 위험을 최소화합니다.
  5. 확장성
    클러스터에 새로운 브로커를 추가하여 쉽게 확장할 수 있습니다.

Kafka의 구성 요소

  1. Topic (토픽)
    데이터를 구분하는 논리적인 채널입니다.
    예: payment-transactions, user-logs.
  2. Producer (생산자)
    데이터를 토픽에 게시하는 역할을 합니다.
  3. Consumer (소비자)
    토픽에서 데이터를 읽어오는 역할을 합니다.
  4. Broker (브로커)
    데이터를 저장하고 전달하는 Kafka 서버입니다.
  5. Partition (파티션)
    토픽을 물리적으로 분할하여 데이터를 저장하고 분산 처리 성능을 향상시킵니다.
  6. Zookeeper
    Kafka 클러스터의 상태를 관리하고 노드 간 동기화를 지원합니다.
    (Kafka 2.8.0부터 Zookeeper 없이도 작동 가능하도록 KRaft(Kafka Raft)로 전환 중)

Kafka의 주요 활용 사례

  1. 실시간 로그 수집 및 처리
    애플리케이션 또는 시스템 로그 데이터를 실시간으로 수집하여 분석.
  2. 실시간 데이터 스트리밍
    금융 거래, IoT 센서 데이터, 소셜 미디어 활동 등을 실시간으로 처리.
  3. 메시지 브로커
    비동기 애플리케이션 간 데이터 전송.
  4. 데이터 파이프라인
    다양한 소스에서 데이터를 수집하고 데이터베이스 또는 데이터 웨어하우스에 저장.

Kafka는 Netflix, LinkedIn, Uber와 같은 대규모 서비스를 운영하는 회사들에서 실시간 데이터 처리를 위해 널리 사용되고 있습니다.

rcv_info_to_kafka.cs
0.00MB

'KAFKA' 카테고리의 다른 글

Kafka 설치 방법  (0) 2025.01.21
APACHE KAFKA 데이타마이그래이션 작업(102)  (0) 2019.10.31

REQUEST)
byte[] messageBytes = new byte[delimiterIndex];
Array.Copy(data, 0, messageBytes, 0, delimiterIndex);
byte[] transactionCodeBytes = messageBytes[0..5];
--------messageBytes[0..5]-------------------------------------------
위는 5바이트 인가요?

RESPONSE)

네, byte[] transactionCodeBytes = messageBytes[0..5];는 5바이트의 배열을 의미합니다.

C#에서 messageBytes[0..5] 구문은 messageBytes 배열의 0번 인덱스부터 5번 인덱스까지의 부분 배열을 생성합니다. 이는 0부터 4까지 인덱스를 포함하므로 총 5개의 바이트가 됩니다.

구체적으로:

  • messageBytes[0..5]는 messageBytes 배열의 0번 인덱스부터 4번 인덱스까지의 바이트를 포함한 배열입니다.
  • 배열 인덱스 범위는 [startIndex..endIndex) 형식이므로, 0..5는 0부터 4까지, 총 5개의 바이트를 포함합니다.

예시:

byte[] messageBytes = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
byte[] transactionCodeBytes = messageBytes[0..5]; // [0, 1, 2, 3, 4] 바이트 포함


REQUEST)
messageBytes[0..4] 가 5바이트 아닌가요?

RESPONSE)

messageBytes[0..4]는 5바이트가 아니라 4바이트입니다.

C#에서 messageBytes[0..4] 구문은 0번 인덱스부터 3번 인덱스까지의 바이트를 포함하는 부분 배열을 생성합니다. 이는 인덱스가 0부터 시작하고, 4는 포함되지 않기 때문에 총 4개의 바이트를 반환합니다.

인덱스 범위 설명:

  • messageBytes[0..4]는 0, 1, 2, 3번 인덱스를 포함하는 부분 배열이므로, 총 4개의 바이트입니다.
  • [start..end) 범위에서 end는 포함되지 않습니다.

예시:

byte[] messageBytes = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
byte[] transactionCodeBytes = messageBytes[0..4]; // [0, 1, 2, 3] 바이트 포함

이 경우, transactionCodeBytes는 [0x01, 0x02, 0x03, 0x04] 값이 포함된 4바이트 배열이 됩니다.

1. HashSet.Add() 중복 허용 여부

HashSet<T>의 Add 메서드는 중복된 값을 허용하지 않습니다.

  • 동작 원리: HashSet은 내부적으로 해시 테이블을 사용하여 데이터를 저장하며, 동일한 값(동일한 해시 값과 동등성을 가진 값)을 추가하려고 하면 false를 반환하고 값을 추가하지 않습니다.
  • 예제:
HashSet<string> aaa = new HashSet<string>();

bool added1 = aaa.Add("Hello"); // 반환값: true (값이 추가됨)
bool added2 = aaa.Add("Hello"); // 반환값: false (중복된 값, 추가되지 않음)

Console.WriteLine(added1); // true
Console.WriteLine(added2); // false

2. HashSet의 주요 용도

HashSet<T>는 주로 다음과 같은 상황에서 사용됩니다:

1) 중복 제거

  • HashSet은 중복된 값을 허용하지 않으므로, 중복 요소를 제거하고 고유한 값을 유지해야 할 때 적합합니다.
  • 예제:
     
string[] items = { "apple", "banana", "apple", "orange", "banana" };
HashSet<string> uniqueItems = new HashSet<string>(items);

foreach (var item in uniqueItems)
{
    Console.WriteLine(item);
}
// 출력: apple, banana, orange

2) 빠른 검색

  • HashSet은 **O(1)**의 시간 복잡도로 항목을 검색합니다. 대량의 데이터를 처리하면서 특정 값의 존재 여부를 빠르게 확인해야 할 때 유용합니다.
  • 예제:
     
HashSet<int> numbers = new HashSet<int> { 1, 2, 3, 4, 5 };

if (numbers.Contains(3))
{
    Console.WriteLine("3이 존재합니다.");
}

3) 집합 연산

  • HashSet은 집합 연산(합집합, 교집합, 차집합)을 효율적으로 처리할 수 있는 메서드를 제공합니다.
    • 합집합: UnionWith
    • 교집합: IntersectWith
    • 차집합: ExceptWith
    • 대칭 차집합: SymmetricExceptWith
  • 예제:
     
HashSet<int> set1 = new HashSet<int> { 1, 2, 3 };
HashSet<int> set2 = new HashSet<int> { 2, 3, 4 };

set1.IntersectWith(set2); // 교집합: { 2, 3 }

foreach (var item in set1)
{
    Console.WriteLine(item); // 출력: 2, 3
}

4) 데이터 필터링

  • 중복된 데이터를 제거하거나 특정 조건에 맞는 고유 데이터를 필터링하는 데 활용됩니다.

3. 장점

  • 빠른 성능: HashSet은 값의 추가, 삭제, 검색이 평균적으로 **O(1)**의 시간 복잡도를 가집니다.
  • 중복 방지: 값의 고유성을 보장합니다.
  • 집합 연산 지원: 데이터 처리 시 유용한 집합 연산을 제공합니다.

4. 단점

  • 순서가 없음: HashSet은 요소의 순서를 보장하지 않습니다. 순서가 중요한 경우 List 또는 SortedSet을 사용하는 것이 적합합니다.
  • 추가적인 메모리 사용: 내부적으로 해시 테이블을 유지하기 때문에 메모리 사용량이 상대적으로 많을 수 있습니다.

5. 결론

HashSet<T>는 중복 제거와 빠른 검색이 필요한 상황, 또는 집합 연산을 효율적으로 수행하려는 경우에 매우 유용합니다. 단, 순서가 필요하지 않은 데이터를 처리할 때 적합합니다.

>정보분배.수신
c# (feat. DOT.NET)


rcv_windows_dotcode.cs
0.00MB



>dotnet new console -o NetPro
>dotnet build
>dotnet run -- 21111


'통신' 카테고리의 다른 글

소켓 프로그래밍(client,server)  (0) 2022.11.20

결론)
REAL Version을 사용하지 않아도 REDIS 버퍼를 활용한, 데이타 저장후 처리는 정상적으로 처리되어짐

TRIAL Version 사용시)
2023-10-28  오전 07:44           553,984 ServiceStack.dll
2023-10-28  오전 07:44            33,792 ServiceStack.Interfaces.dll
2023-10-28  오전 07:44            20,480 ServiceStack.ServiceInterface.dll

Exception 발생경우)

정상적인 Insert로직 실행
Exception발생하지 않는 경우는) REDIS로 전송

[2025-01-21 10:22:46]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:47]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:47]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:48]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:48]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:48]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:49]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:49]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:50]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:22:50]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:23:31]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
[2025-01-21 10:24:01]>(MultipleRedisManager)(SendQuery)(JsonConvert.SerializeObject)(Exception):The free-quota limit on '6000 Redis requests per hour' has been reached. Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.
    try
    {
        sendRealRedis.AddItemToList("REAL", ____query);
    }
    catch (InvalidOperationException ex)
    {
         return (null);  //정상적인 Insert로직 실행하도록 NULL리턴
    }
    catch (Exception ex)
    {
         return (null);  //정상적인 Insert로직 실행하도록 NULL리턴
    }

 

결론)
REAL Version을 사용하지 않아도 REDIS 버퍼를 활용한, 데이타 저장후 처리는 정상적으로 처리되어짐

AppDomain.CurrentDomain.BaseDirectory로 얻은 디렉토리 경로에 /Data/CreateDB.sql 파일의 경로를 추가하려면, Path.Combine() 메서드를 사용하여 경로를 안전하게 결합할 수 있습니다.

다음은 currentDirectory에 /Data/CreateDB.sql 파일의 경로를 결합하고 이를 currentFilename 변수에 저장하는 코드 예시입니다:

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        // 현재 실행 중인 프로그램의 경로 (dotnet run 실행 폴더)
        string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;

        // 하위 디렉토리와 파일 경로를 결합
        string currentFilename = Path.Combine(currentDirectory, "Data", "CreateDB.sql");

        // 결과 출력
        Console.WriteLine($"현재 파일 경로: {currentFilename}");
    }
}

설명:

  • AppDomain.CurrentDomain.BaseDirectory: 현재 실행 중인 프로그램의 경로를 반환합니다.
  • Path.Combine(): 여러 경로 요소를 결합하여 올바른 경로 문자열을 생성합니다. 이 방법은 운영 체제에 맞는 경로 구분자를 자동으로 사용하므로 \ 또는 /를 직접 처리할 필요가 없습니다.
    • "Data"는 BaseDirectory의 하위 디렉터리입니다.
    • "CreateDB.sql"은 그 디렉터리 내의 파일입니다.

결과:

  • 위 코드에서 currentFilename은 currentDirectory 경로와 Data/CreateDB.sql을 결합한 절대 경로를 가지게 됩니다. 예를 들어, currentDirectory가 C:\MyApp\인 경우, currentFilename은 C:\MyApp\Data\CreateDB.sql이 됩니다.

참고:

Path.Combine()은 각 부분을 안전하게 결합하여 운영 체제에 맞는 경로를 생성하므로, 경로 구분자를 직접 추가하거나 처리할 필요가 없어 더 안전하고 권장되는 방법입니다.

 
 
 
 
class Program
{
    public static void Main()
    {
        CHKNM nmm = new CHKNM();
nmm.RUN();
    }
}
class CHKNM
{
    public void RUN()
    {
        string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
            // 하위 디렉토리와 파일 경로를 결합
            string currentFilename = Path.Combine(currentDirectory, "Data", "CreateDB.sql");
    string currentEstFilename = Path.Combine(currentDirectory, "Data", "CreateEstateDB.sql");


    Console.WriteLine(currentDirectory);
    Console.WriteLine(currentFilename);
    Console.WriteLine(currentEstFilename);
    }
}

C:\Users\xterm\Downloads\dotnetconsole90>dotnet run
C:\Users\xterm\Downloads\dotnetconsole90\dotnetconsole90.csproj : warning NU1803: 'http://nuget.grapecity.com/nuget' 'HTTP' 원본을 사용하여 'restore' 작업을 실 행하고 있습니다. 비
 HTTPS 액세스는 이후 버전에서 제거됩니다. 'HTTPS' 원본으로 마이그레이션하는 것이 좋습니다.
C:\Users\xterm\Downloads\dotnetconsole90\dotnetconsole90.csproj : warning NU1803: 'http://nuget.grapecity.com/nuget' 'HTTP' 원본을 사용하여 'restore' 작업을 실 행하고 있습니다. 비
 HTTPS 액세스는 이후 버전에서 제거됩니다. 'HTTPS' 원본으로 마이그레이션하는 것이 좋습니다.
C:\Users\xterm\Downloads\dotnetconsole90\bin\Debug\net8.0\
C:\Users\xterm\Downloads\dotnetconsole90\bin\Debug\net8.0\Data\CreateDB.sql
C:\Users\xterm\Downloads\dotnetconsole90\bin\Debug\net8.0\Data\CreateEstateDB.sql

C:\Users\xterm\Downloads\dotnetconsole90>

T'way Holdings 의 <'> 특수문자 테이블에 입력하기

{
{"A0","01S","00000332","001034","20230418","KR7004870002","000332","A004870  ","?곗썾?댄??⑹뒪                            ","T'way Holdings                          ","00005","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","032303    ","N"," ","0","00000000646","1","00000000646","000000441839","000000000283734992.000","00000000839","00000000453","00000000450","0000500.000","00000000000","19770630","0000000109472167","N","        ","        ","        ","        ","000000000.000","000000054736083500.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000070.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000001547988.000","N","N","N"," ","}",
{"A0","01S","00000333","001034","20230418","KR7004890000","000333","A004890  ","?숈씪?곗뾽                                ","DIC                                     ","00005","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","032401    ","N"," ","0","00000218000","1","00000218000","000000001716","000000000374394000.000","00000283000","00000153000","00000165680","0005000.000","00000024000","20050630","0000000002425215","N","        ","        ","        ","        ","000000000.000","000000012126075000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","Y","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000076.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000024253.000","N","N","N"," ","}",
{"A0","01S","00000334","001034","20230418","KR7004910006","000334","A004910  ","議곌킅?섏씤??                             ","ChokwangPaint                           ","00003","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","032004    ","N"," ","0","00000007810","1","00000007810","000000040114","000000000313154770.000","00000010150","00000005470","00000005310","0000500.000","00000000000","19761227","0000000012800000","N","        ","        ","        ","        ","000000000.000","000000006400000000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000068.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000128041.000","N","N","N"," ","}",
{"A0","01S","00000335","001034","20230418","KR7004920005","000335","A004920  ","?⑥븘?댄뀒??                             ","CITECH                                  ","00003","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","033309    ","N"," ","0","00000001447","1","00000001447","000001789100","000000002627682556.000","00000001881","00000001013","00000000980","0000500.000","00000000000","19891130","0000000046403359","N","        ","        ","        ","        ","000000000.000","000000023201679500.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000068.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000691086.000","N","N","N"," ","}",
{"A0","01S","00000336","001034","20230418","KR7004960001","000336","A004960  ","?쒖떊怨듭쁺                                ","HanshinConst                            ","00004","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","064101    ","N"," ","0","00000007980","1","00000007980","000000113531","000000000914082160.000","00000010370","00000005590","00000005580","0005000.000","00000000000","19760707","0000000011570702","N","        ","        ","        ","        ","000000000.000","000000057853510000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","N","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000070.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000125314.000","N","N","N"," ","}",
{"A0","01S","00000337","001034","20230418","KR7004970000","000337","A004970  ","?좊씪援먯뿭                                ","Silla                                   ","00004","STK","ST","N","00","00","N","00","N","1","N","00","N","N","N","N","N","010301    ","N"," ","0","00000010570","1","00000010570","000000006385","000000000067294450.000","00000013740","00000007400","00000007610","0000500.000","00000000000","19760630","0000000016000000","N","        ","        ","        ","        ","000000000.000","000000008000000000.000","Y","00007","00007","00001","00007","00001","00","0","N","00000000000","00000000000","00000000000","00000000001","00000000001"," ","            ","KRW","410","Y","Y","Y","Y","Y","Y","N","000000.000000","N","N","0","000072.000000","N","        ","0"," ","        ","  ","        ","        "," ","  ","      ","   ","  ","  ","      ","      ","     ","N","N","N","0000000000000160000.000","N","N","N"," ","}",

,,
,,
};


A001S0000033200103420230418KR7004870002000332A004870  티웨이홀딩스                            T'way Holdings                          00005STKSTN0000N00N1N00

https://www.investing.com/equities/tway-holdings
TWay Holdings (004870)



프로시져.테이블내의 13개의 필드에 대한 1000줄의 입력데이타를 처리

CREATE TABLE [EXTUREPLUSSTOCKFOREIGNERMASTER] (
[DATACLASS] VARCHAR2(2),
[INFOMARKETCLASS] VARCHAR2(3),
[STOCKCODE] VARCHAR2(12),
[SERIALNO] VARCHAR2(8),
[TRADEDATE] VARCHAR2(8),
[STOCKLIMITRATE] VARCHAR2(5),
[INDIVIDUALLIMITRATE] VARCHAR2(5),
[LISTINGSTOCKSIZE] VARCHAR2(15),
[ORDERPOSSIBILITYVOLUME] VARCHAR2(15),
[LIMITEXHAUSTCLASS] VARCHAR2(1),
[FILLER1] VARCHAR2(5),
[FILLER2] VARCHAR2(5),

[ENDTEXT] VARCHAR2(1)
);

구분자 방식("N:12345"와 "S:Hello World")을 적용하여 숫자와 문자열을 구분하려면, C/C++ 코드에서 구분자(N: 또는 S:)를 활용하여 데이터를 구분하고, Oracle 프로시저에서 이를 처리하도록 할 수 있습니다. 여기서는 두 가지 주요 방법을 설명하겠습니다:

  1. C/C++ 코드에서 데이터 전처리
  2. Oracle 프로시저에서 데이터 처리

1. C/C++ 코드에서 데이터 전처리

C/C++에서는 입력 데이터를 N:과 S:로 구분하여 Oracle 프로시저로 전달할 수 있도록 준비합니다. 이를 위해 inData 배열을 처리하고, 각 값에 대해 N:은 숫자, S:는 문자열로 판단하여 Oracle 프로시저에 전달합니다.

예제: C/C++ 코드에서 구분자 추가

 
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char inData[1000][13][100];

void preprocessData(char rawData[1000][13][100]) {
    // 배열 초기화 및 데이터 전처리
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 13; j++) {
            if (isdigit(rawData[i][j][0])) { // 숫자인 경우
                snprintf(inData[i][j], 100, "N:%s", rawData[i][j]);
            } else { // 문자열인 경우
                snprintf(inData[i][j], 100, "S:%s", rawData[i][j]);
            }
        }
    }
}

// 예시 호출
int main() {
    char rawData[1000][13][100] = {
        {"12345", "Hello", "67890", "World", "1", "2", "3", "4", "5", "6", "7", "8", "9"},
        {"54321", "Test", "98765", "Example", "10", "20", "30", "40", "50", "60", "70", "80", "90"}
        // 기타 데이터 추가...
    };
    
    preprocessData(rawData);
    
    // 'inData' 배열을 Oracle 프로시저로 전달
}



위 코드에서 preprocessData 함수는 rawData 배열을 받아 각 값에 N: 또는 S:를 접두사로 추가하여 inData 배열에 저장합니다. 이렇게 하면 inData 배열은 숫자와 문자열을 구분할 수 있는 형식으로 변환됩니다.


2. Oracle 프로시저에서 데이터 처리

Oracle 프로시저에서는 전달된 데이터를 구분자(N: 또는 S:)에 따라 숫자와 문자열을 구분해서 처리할 수 있습니다. 이때 문자열의 앞 두 문자를 확인하여 숫자와 문자열을 판별하고, 그에 맞는 처리 로직을 수행합니다.

예제: Oracle 프로시저에서 구분자 처리

CREATE OR REPLACE PROCEDURE bulk_insert_proc (
    p_data IN SYS.ODCIVARCHAR2LIST -- 문자열 배열 타입
)
IS
    v_prefix VARCHAR2(2);
    v_value  VARCHAR2(4000);
BEGIN
    FOR i IN 1..p_data.COUNT / 13 LOOP
        FOR j IN 1..13 LOOP
            v_prefix := SUBSTR(p_data((i-1)*13 + j), 1, 2);  -- 앞의 구분자 확인
            v_value := SUBSTR(p_data((i-1)*13 + j), 3);      -- 구분자 이후의 값
            
            IF v_prefix = 'N:' THEN
                -- 숫자 처리: 여기서는 예를 들어 숫자 컬럼에 값을 삽입
                INSERT INTO EXTUREPLUSSTOCKFOREIGNERMASTER (number_column)
                VALUES (TO_NUMBER(v_value));
            ELSIF v_prefix = 'S:' THEN
                -- 문자열 처리: 문자열 컬럼에 값을 삽입
                INSERT INTO EXTUREPLUSSTOCKFOREIGNERMASTER (string_column)
                VALUES (v_value);
            END IF;
        END LOOP;
    END LOOP;
    
    COMMIT;
END;
/

프로시저 설명:

  1. 구분자 확인: SUBSTR 함수로 문자열의 처음 두 글자인 N: 또는 S:를 확인하여, 숫자인지 문자열인지를 구분합니다.
  2. 숫자 처리: N:으로 시작하는 값은 TO_NUMBER를 사용해 숫자로 변환하여 숫자 컬럼에 삽입합니다.
  3. 문자열 처리: S:로 시작하는 값은 그대로 문자열로 처리하여 문자열 컬럼에 삽입합니다.

3. C/C++에서 Oracle 프로시저 호출

C/C++ 코드에서 위와 같이 전처리된 inData 배열을 Oracle 프로시저로 전달할 수 있습니다. 이때 각 레코드는 13개의 값을 가지며, 각 값은 N: 또는 S:로 시작하는 형태로 전달됩니다.

예제: Oracle 프로시저 호출

#include <oci.h>

// OCI 핸들 및 환경 초기화
OCIEnv     *env;
OCIError   *err;
OCISvcCtx  *svc;
OCIStmt    *stmt;
OCIBind    *bind;
OCIArray   *array_data;

void bulkInsert(char inData[1000][13][100]) {
    // 환경 초기화
    OCIEnvCreate(&env, OCI_THREADED | OCI_OBJECT, NULL, NULL, NULL, NULL, 0, NULL);
    OCIHandleAlloc(env, (void **)&err, OCI_HTYPE_ERROR, 0, NULL);
    OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);

    // Oracle 연결
    OCILogon(env, err, &svc, (text *)"username", strlen("username"), (text *)"password", strlen("password"), (text *)"db", strlen("db"));

    // 배열 준비: 1000 레코드, 13 컬럼
    OCIDescriptorAlloc(env, (void **)&array_data, OCI_DTYPE_TABLE, 0, NULL);
    
    // 데이터를 배열에 삽입
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 13; j++) {
            OCIArrayAppend(array_data, (text *)inData[i][j], strlen(inData[i][j]), err);
        }
    }

    // 프로시저 호출 준비
    OCIStmtPrepare(stmt, err, (text *)"BEGIN bulk_insert_proc(:data); END;", strlen("BEGIN bulk_insert_proc(:data); END;"), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 바인딩
    OCIBindByName(stmt, &bind, err, (text *)":data", strlen(":data"), (void *)array_data, sizeof(array_data), SQLT_TAB, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);

    // 실행
    OCIStmtExecute(svc, stmt, err, 1, 0, NULL, NULL, OCI_DEFAULT);

    // 종료
    OCILogoff(svc, err);
    OCIHandleFree(stmt, OCI_HTYPE_STMT);
    OCIHandleFree(err, OCI_HTYPE_ERROR);
    OCIHandleFree(svc, OCI_HTYPE_SVCCTX);
    OCIEnvFree(env, OCI_DEFAULT);
}

결론

  • C/C++ 코드에서 데이터를 N:과 S:로 구분하여 Oracle 프로시저에 전달합니다.
  • Oracle 프로시저에서는 SUBSTR을 사용하여 구분자를 확인하고, 숫자와 문자열을 처리합니다.
  • N: 접두사는 숫자 컬럼에 삽입하고, S: 접두사는 문자열 컬럼에 삽입합니다.

프로시져.테이블내의 13개의 필드에 대한 1000줄의 입력데이타를 처리

CREATE TABLE [EXTUREPLUSSTOCKFOREIGNERMASTER] (
[DATACLASS] VARCHAR2(2),
[INFOMARKETCLASS] VARCHAR2(3),
[STOCKCODE] VARCHAR2(12),
[SERIALNO] VARCHAR2(8),
[TRADEDATE] VARCHAR2(8),
[STOCKLIMITRATE] VARCHAR2(5),
[INDIVIDUALLIMITRATE] VARCHAR2(5),
[LISTINGSTOCKSIZE] VARCHAR2(15),
[ORDERPOSSIBILITYVOLUME] VARCHAR2(15),
[LIMITEXHAUSTCLASS] VARCHAR2(1),
[FILLER1] VARCHAR2(5),
[FILLER2] VARCHAR2(5),

[ENDTEXT] VARCHAR2(1)
);

구분자 방식("N:12345"와 "S:Hello World")을 적용하여 숫자와 문자열을 구분하려면, C/C++ 코드에서 구분자(N: 또는 S:)를 활용하여 데이터를 구분하고, Oracle 프로시저에서 이를 처리하도록 할 수 있습니다. 여기서는 두 가지 주요 방법을 설명하겠습니다:

  1. C/C++ 코드에서 데이터 전처리
  2. Oracle 프로시저에서 데이터 처리

1. C/C++ 코드에서 데이터 전처리

C/C++에서는 입력 데이터를 N:과 S:로 구분하여 Oracle 프로시저로 전달할 수 있도록 준비합니다. 이를 위해 inData 배열을 처리하고, 각 값에 대해 N:은 숫자, S:는 문자열로 판단하여 Oracle 프로시저에 전달합니다.

예제: C/C++ 코드에서 구분자 추가

 
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char inData[1000][13][100];

void preprocessData(char rawData[1000][13][100]) {
    // 배열 초기화 및 데이터 전처리
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 13; j++) {
            if (isdigit(rawData[i][j][0])) { // 숫자인 경우
                snprintf(inData[i][j], 100, "N:%s", rawData[i][j]);
            } else { // 문자열인 경우
                snprintf(inData[i][j], 100, "S:%s", rawData[i][j]);
            }
        }
    }
}

// 예시 호출
int main() {
    char rawData[1000][13][100] = {
        {"12345", "Hello", "67890", "World", "1", "2", "3", "4", "5", "6", "7", "8", "9"},
        {"54321", "Test", "98765", "Example", "10", "20", "30", "40", "50", "60", "70", "80", "90"}
        // 기타 데이터 추가...
    };
    
    preprocessData(rawData);
    
    // 'inData' 배열을 Oracle 프로시저로 전달
}



위 코드에서 preprocessData 함수는 rawData 배열을 받아 각 값에 N: 또는 S:를 접두사로 추가하여 inData 배열에 저장합니다. 이렇게 하면 inData 배열은 숫자와 문자열을 구분할 수 있는 형식으로 변환됩니다.


2. Oracle 프로시저에서 데이터 처리

Oracle 프로시저에서는 전달된 데이터를 구분자(N: 또는 S:)에 따라 숫자와 문자열을 구분해서 처리할 수 있습니다. 이때 문자열의 앞 두 문자를 확인하여 숫자와 문자열을 판별하고, 그에 맞는 처리 로직을 수행합니다.

예제: Oracle 프로시저에서 구분자 처리

CREATE OR REPLACE PROCEDURE bulk_insert_proc (
    p_data IN SYS.ODCIVARCHAR2LIST -- 문자열 배열 타입
)
IS
    v_prefix VARCHAR2(2);
    v_value  VARCHAR2(4000);
BEGIN
    FOR i IN 1..p_data.COUNT / 13 LOOP
        FOR j IN 1..13 LOOP
            v_prefix := SUBSTR(p_data((i-1)*13 + j), 1, 2);  -- 앞의 구분자 확인
            v_value := SUBSTR(p_data((i-1)*13 + j), 3);      -- 구분자 이후의 값
            
            IF v_prefix = 'N:' THEN
                -- 숫자 처리: 여기서는 예를 들어 숫자 컬럼에 값을 삽입
                INSERT INTO EXTUREPLUSSTOCKFOREIGNERMASTER (number_column)
                VALUES (TO_NUMBER(v_value));
            ELSIF v_prefix = 'S:' THEN
                -- 문자열 처리: 문자열 컬럼에 값을 삽입
                INSERT INTO EXTUREPLUSSTOCKFOREIGNERMASTER (string_column)
                VALUES (v_value);
            END IF;
        END LOOP;
    END LOOP;
    
    COMMIT;
END;
/

프로시저 설명:

  1. 구분자 확인: SUBSTR 함수로 문자열의 처음 두 글자인 N: 또는 S:를 확인하여, 숫자인지 문자열인지를 구분합니다.
  2. 숫자 처리: N:으로 시작하는 값은 TO_NUMBER를 사용해 숫자로 변환하여 숫자 컬럼에 삽입합니다.
  3. 문자열 처리: S:로 시작하는 값은 그대로 문자열로 처리하여 문자열 컬럼에 삽입합니다.

3. C/C++에서 Oracle 프로시저 호출

C/C++ 코드에서 위와 같이 전처리된 inData 배열을 Oracle 프로시저로 전달할 수 있습니다. 이때 각 레코드는 13개의 값을 가지며, 각 값은 N: 또는 S:로 시작하는 형태로 전달됩니다.

예제: Oracle 프로시저 호출

#include <oci.h>

// OCI 핸들 및 환경 초기화
OCIEnv     *env;
OCIError   *err;
OCISvcCtx  *svc;
OCIStmt    *stmt;
OCIBind    *bind;
OCIArray   *array_data;

void bulkInsert(char inData[1000][13][100]) {
    // 환경 초기화
    OCIEnvCreate(&env, OCI_THREADED | OCI_OBJECT, NULL, NULL, NULL, NULL, 0, NULL);
    OCIHandleAlloc(env, (void **)&err, OCI_HTYPE_ERROR, 0, NULL);
    OCIHandleAlloc(env, (void **)&svc, OCI_HTYPE_SVCCTX, 0, NULL);

    // Oracle 연결
    OCILogon(env, err, &svc, (text *)"username", strlen("username"), (text *)"password", strlen("password"), (text *)"db", strlen("db"));

    // 배열 준비: 1000 레코드, 13 컬럼
    OCIDescriptorAlloc(env, (void **)&array_data, OCI_DTYPE_TABLE, 0, NULL);
    
    // 데이터를 배열에 삽입
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 13; j++) {
            OCIArrayAppend(array_data, (text *)inData[i][j], strlen(inData[i][j]), err);
        }
    }

    // 프로시저 호출 준비
    OCIStmtPrepare(stmt, err, (text *)"BEGIN bulk_insert_proc(:data); END;", strlen("BEGIN bulk_insert_proc(:data); END;"), OCI_NTV_SYNTAX, OCI_DEFAULT);

    // 바인딩
    OCIBindByName(stmt, &bind, err, (text *)":data", strlen(":data"), (void *)array_data, sizeof(array_data), SQLT_TAB, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);

    // 실행
    OCIStmtExecute(svc, stmt, err, 1, 0, NULL, NULL, OCI_DEFAULT);

    // 종료
    OCILogoff(svc, err);
    OCIHandleFree(stmt, OCI_HTYPE_STMT);
    OCIHandleFree(err, OCI_HTYPE_ERROR);
    OCIHandleFree(svc, OCI_HTYPE_SVCCTX);
    OCIEnvFree(env, OCI_DEFAULT);
}

결론

  • C/C++ 코드에서 데이터를 N:과 S:로 구분하여 Oracle 프로시저에 전달합니다.
  • Oracle 프로시저에서는 SUBSTR을 사용하여 구분자를 확인하고, 숫자와 문자열을 처리합니다.
  • N: 접두사는 숫자 컬럼에 삽입하고, S: 접두사는 문자열 컬럼에 삽입합니다.


프로시져로 전달한 데이타의 숫자,문자열구분

입력 데이터에 구분자 추가

입력 데이터를 C/C++에서 Oracle로 전달하기 전에, 숫자와 문자열을 구분하는 구분자를 추가합니다. 예를 들어:

  • 숫자는 "N:<value>" 형태
  • 문자열은 "S:<value>" 형태

프로시저 수정

CREATE OR REPLACE PROCEDURE bulk_insert_proc (
    p_data IN SYS.ODCIVARCHAR2LIST
)
IS
    v_prefix VARCHAR2(1);
    v_value  VARCHAR2(4000);
BEGIN
    FOR i IN 1..p_data.COUNT LOOP
        v_prefix := SUBSTR(p_data(i), 1, 2); -- 앞의 구분자 확인
        v_value := SUBSTR(p_data(i), 3);    -- 구분자 이후의 값

        IF v_prefix = 'N:' THEN
            INSERT INTO your_table (number_column, string_column)
            VALUES (TO_NUMBER(v_value), NULL);
        ELSIF v_prefix = 'S:' THEN
            INSERT INTO your_table (number_column, string_column)
            VALUES (NULL, v_value);
        END IF;
    END LOOP;

    COMMIT;
END;
/
 


C/C++ 코드에서 데이터 예시
:

char insertmsg[1000][1024] = {
    "N:12345", // 숫자
    "S:Hello World", // 문자열
    ...
};
 

+ Recent posts