올해 처음 종합 소득세 납부를 해봤습니다.

이름이 종합 소득세 이기때문에 모든 소득에 대해 종합하여 이득을 보는 금액을 납부하는 것은 아닙니다.

저도 작년에 국내 주식, 해외 주식을 했는데 합하면 마이너스입니다.

그렇지만 종합 소득세는 납부합니다.

이게 뭔~ 이상한 소리냐구요.

종합 소득세는 배당소득, 이자소득 등에 대한 세금입니다.

그래서 국내주식, 해외주식으로 배당을 받은 경우 세금을 납부하게 됩니다.

대신 2,000만원 이하일 경우 분리과세로 종결이 가능하고 넘게 되면 

국내주식 및 해외 수직으로 매수 매도 차익이 마이너스라고 배당소득, 이자 소득이 있으면 세금을 납부해야 한다는 소리입니다.

즉, 종합 소득세라는 말 때문에 모든 것을 종합하여 이익이 발생하였을 때 내는 세금은 아니라는 소리입니다.

매수 매도 차익은 양도 소득세로 내며 국내주식 + 해외 주식에 대해 합산하여 내는 세금은 또 아닙니다.

국내주식 + 해외 주식 매매 차익이 마이너스라도 세금은 납부할 수 있습니다.

이 또한 뭔~~  이상한 소리냐구요.

해외 주식 양도 소득세라는 것이 있어서 해외 주식에 대해 매매 차익이 플러스이면 세금을 냅니다.

국내수직 매매차익을 합산하지는 않습니다.

물론 저는 작년에 수익이 다음과 같았지만 

국내주식 매매 차익(마이너스) + 해외주식 매매 차익(플러스) = 전체 매매차익(마이너스)

해외주식 매매 차익이 플러스 이기 때문에 해외 주식 양도 소득세를 납부하라고 알림/메일을 받았습니다.

즉, 분리 과세라는 말입니다.

1) 해외 주식 양도 소득세 와 종합 소득세 차이점

해외 주식 양도세와 종합 소득세의 차이점을 정리해보았습니다.

2025년은 **5월 31일(토)**이 주말이고, **6월 1일(일)**도 휴일이기 때문에 **종합소득세 및 해외주식 양도소득세 신고 마감일이 2025년 6월 2일(월)**로 연장됩니다.

💡 해외 주식 양도소득세 vs 종합소득세 (이자·배당 포함) — 2025년 기준

항목 해외 주식 양도소득세 종합소득세 (배당·이자소득 포함)
과세 대상 해외 주식 매도 차익 배당소득, 이자소득, 근로·사업 등 종합소득
과세 방식 분리과세 (기타소득으로 분류) 종합과세 (다른 소득과 합산하여 누진세율 적용)
세율 연 250만원 공제 후 22% (지방세 포함) 원천징수 15.4%, 종합과세 선택 시 6.6%~49.5% 누진세율 적용
공제 연 250만원까지 양도차익 비과세 금융소득(이자+배당)이 2,000만원 이하일 경우 분리과세로 종결 가능
신고 시기 다음 해 5월 1일 ~ 2025년 6월 2일(월) 다음 해 5월 1일 ~ 2025년 6월 2일(월)
신고 방식 직접 신고 또는 증권사 대행 신고 가능 (예: 키움증권) 금융소득종합과세 대상자만 직접 신고 필요
신고 필요 여부 매도 차익 발생 시 자진신고 필요 (단, 키움 등에서 대행 가능) 금융소득(이자+배당)이 연 2,000만원 초과 시 자진신고
납부 방식 자진 신고 및 납부 (또는 대행 납부) 자진 신고 및 납부 (기본은 원천징수로 납부 완료)
예시 미국 주식 매도 차익 발생 → 키움 통해 신고 대행 가능 미국 주식 배당금, 국내외 예금이자 등 수령
 

2) 종합 소득세 공지 메일 

키움증권을 사용하는데 종합소득세 납부를 해야한다는 공지 알림을 받았습니다.

해외주식 양도소득세는 대행신고가 가능하지만 종합소득세는 대행신고는 되지 않는다고 합니다.

대신 증권사에서 국세청으로 자료 제공을 한다고 합니다.

2025년의 경우 종합 소득세는 6월 2일까지 신고 및 납부를 해야 합니다.

5월 22일에 키움 증권에서 "[키움증권] 종합과세대상 홈택스 금융소득 신고 안내" 라는 메일을 받았습니다.

해당 메일에는 홈택스에서 종합 소득세 신고를 하는 방법이 자세히 나와 있었습니다.

 

3) 홈택스 > 종합 소득세 신고 > 종합소득세신고

홈택스에 접속합니다.

세금신고 > 종합소득세 신고 > 종합소득세신고(모두채음.일반.근로.분리과세.종교인포함 및 중간예납.토지등 매치차익) 순으로 클릭합니다.

 

그런데 해당 서비스는 서비스 중지 시간이 있습니다.

해당 시간에 클릭하면 다음과 같이 메시지가 나오네요.

 

세금신고는 06:00 ~ 24:00 이고 세금납부는 07:00 ~ 23:30이네요.

그래서 세금신고는 06:00 ~ 24:00 시간에 다시 접속했습니다.

처음 종합소득세 신고를 선택하면 모두채움신고도 있고 일반신고에 정신신고도 있습니다.

주로 모두채움신고와 정기신고를 선택하여 진행합니다.

모두 채움 과 정기 신고(확정 신고) 의 차이점을 깔끔히 정리해 보겠습니다.


🟦 “모두 채움” 신고

  • 정식 명칭: 국세청에서 제공하는 “모두채움 신고서” (또는 “모두채움 서비스”)
  • 대상: 주로 단순한 소득(근로소득만 있거나, 사업소득이 단순한 경우 등)
  • 특징:
    ✔️ 국세청이 이미 보유하고 있는 소득·공제 자료를 바탕으로 신고서가 자동 작성
    ✔️ 납세자가 수정·확인 후 제출만 하면 됨
    ✔️ 비교적 단순한 경우에만 가능 (복잡한 경우는 수동으로 신고서 작성해야 함)
  • 신고서: 기본적으로 ‘정기 신고(확정 신고)’와 동일하지만, 단순한 경우라면 국세청이 자동 작성해준 것에 납세자가 ‘확인/보완’만 하면 된다는 점이 다름

🟦 정기 신고 (확정 신고)

  • 의미: 전년도 종합소득을 기준으로 최종 세액을 직접 신고하는 것
  • 대상: 모든 종합소득세 납세자
  • 특징:
    ✔️ 소득·공제 자료를 본인이 직접 입력해 신고서 작성
    ✔️ 국세청 제공 자료를 참고하긴 하지만, 납세자가 스스로 신고서를 작성해야 함

 주요 차이점 정리

항목 모두 채움 신고 정기 신고(확정 신고)
개념 국세청이 신고서를 자동 작성해줌 납세자가 직접 신고서를 작성
신고서 작성 국세청 자료 기반으로 자동 채움 납세자가 자료를 수집·입력해 작성
편의성 훨씬 간편, 수정·확인만 하면 됨 직접 작성이므로 좀 더 복잡
대상 단순소득자, 근로소득만 있거나 간단한 경우 모든 종합소득세 신고 대상자
 

🎯 결론

✔️ “모두 채움” = 정기 신고의 간소화 버전
✔️ 본질은 둘 다 종합소득세 확정 신고지만,
✔️ 모두채움은 국세청이 미리 자료를 채워준 신고서를 납세자가 수정·확인하는 방식이라는 점이 차이점입니다.

 

저는 처음 종합소득세 신고 화면 진입시에 아래와 같이 종합소득세 확정신고 대상으로 안내받았습니다.

라고 뜨네요.

[예(신고하기)]를 클릭하였습니다.

[예(신고하기)]를 클릭 시 아래와 같은 화면으로 이동이 됩니다.

만약 위와 같은 확정신고 대상으로 안내받았습니다.

라는 것이 뜨지 않으면 일반적으로 일반신고 > 정기신고를 선택합니다.

4) 기본정보 입력

다음은 기본정보 입력화면입니다.

주민등록번호 앞자리를 확인 후에 맞으면 [확인]을 클릭합니다.

확인을 클릭하면 주소, 휴대전화 등이 업데이트 됩니다.

해당 내용들이 맞는지 확인 후에 [작성완료]를 클릭합니다.

5) 종합(분리)소득금액 > 나의 소득종류 찾기

기본정보 [작성완료]를 누르면 종합(분리)소득금액을 입력하는 화면으로 이동합니다.

[나의소득종류 찾기]를 클릭합니다.

 

근로소득이 있는 직장인이고 은행이자, 주식 배당 소득이 2,000만원 초과인 종합과세대상자는 근로소득, 이자소득, 배당소득을 클릭합니다.

직장인은 연말정산을 따로하지만 근로소득도 같이 체크해주어야 한다고 합니다.

✔️ 근로소득 체크

  • 근로소득: 이미 연말정산을 했더라도 종합소득세 신고서에는 포함되어야 전체 종합소득이 계산됩니다.
    → 단, 이미 회사에서 연말정산한 경우에는 납부세액이 자동 반영되고, 다시 세금을 내야 하는 건 아닙니다.

✅ 반드시 종합소득세 신고를 해야 하고, 홈택스에서 다음 항목을 모두 체크해야 합니다:

항목 체크 여부 이유
근로소득 ✅ 체크 종합소득 전체 계산을 위해 포함 필요 (연말정산 했더라도)
배당소득 ✅ 체크 2천만 원 초과로 종합과세 대상
이자소득 ✅ 체크 배당과 합쳐 2천만 원 초과 → 종합과세 대상

저는 소득금액 100만원 이상인 직장인 근로자의 경우를 예시한 것입니다.  개인마다 다를 수 있으므로 체크항목이 달라질 수 있습니다.

참고로 키움증권에서 메일로 받은 종합소득세 신고에서는 이자소득, 배당소득만 체크하는 것으로 이미지가 나와 있었습니다.

복수근로 소득자 또는 소득금액 100만원 이상자에 대해서는 예시를 하지 않은 것으로 생각됩니다.

즉, 공지로 온 메일 그대로 따라하기 보다는 본인의 소득종류를 확인 후에 체크를 해야 한다는 것입니다.

[적용하기]를 클릭합니다.

 

6) 종합(분리)소득금액 > 근로소득

국세청에서 보유한 자료 가져오기가 자동으로 되면서 내역이 보입니다.

내역을 체크 후에 [선택완료]를 클릭합니다.

 

7) 종합(분리)소득금액 > 금융(이자·배당)소득

금융(이자·배당)소득에서 입력하기를 클릭합니다.

[국세청에서 보유한 자료 가져오기]를 클릭합니다.

국세청에서 보유한 자료 가져오기를 하면 금액이 자동적으로 입력이 됩니다.

저는 내용 확인 후에 [선택완료]를 클릭하였습니다.

이자소득 및 배당소득이 정상적으로 입력되었는지 확인이 필요합니다.

뭐~~ 대부분은 잘 입력이 되겠지만 누락이되는 경우도 심심치 않게 발생합니다.

배당소득의 경우 각 증권사에서 확인하는 방법이 존재합니다.

누락없이 정상적으로 자동입력이 되었는지 확인이 필요합니다.

누락되었다고 증권사에서 피해보상을 해주지는 않을 것 같습니다.

키움증권의 경우 해외주식 양도소득세의 경우는 대행신고를 해주지만 종합소득세는 대행신고를 해주지 않습니다.

일부 다른 증권사는 종합소득세도 대행신고를 해주는 곳도 존재하는 것 같습니다.

암튼~ 피해의 책임이 개인에게 돌아가는 경우가 많으므로 개인 스스로 잘 챙겨야 합니다.

8) 종합(분리)소득금액 > 금융(이자·배당)소득 > 증권사와 비교

키움증권은 다음과 같이 금융소득내역서 발급이 가능합니다.

홈페이지 : 전체메뉴 - 뱅킹/업무 - 서류발급/조회 - 금융소득내역서 발급(즉시발급/우편/이메일)
영웅문4 : 온라인업무 - 고객업무신청 - [1105] 금융소득내역서 서류발급(우편/이메일) 에서도 신청 가능하오니 참고하시기 바랍니다.

위와 같이 신청을 하면 다음과 같이 조회가 가능합니다.

구분에서 T, G 가 있습니다.

T 는 배당가산(Gross-Up)하지 아니하는 배당소득이고

G는 배당가산(Gross-Up)하는 배당소득입니다.

그 차이점을 테이블 형태로 자세히 정리해 드리겠습니다.



구분 배당가산하지 않는 배당소득(T) 배당가산하는 배당소득(G)
적용되는 배당소득 - 집합투자기구 배당소득 (예: 펀드 이자소득 등)
- 외국법인으로부터 받는 배당소득
- 국내법인으로부터 받는 배당소득
가산 여부 배당가산(=Gross-Up)하지 않음 배당가산하여 계산 (배당소득에 일정 비율을 더해 과세 표준에 포함)
과세표준 계산 방식 실제 배당금액을 그대로 과세표준에 반영 실제 배당금액 + (배당금액 × 11%)
즉, 배당소득을 11% 추가하여 과세표준에 반영
세율 적용 배당금액에 대해 기본 세율(종합소득세율 또는 14% 분리과세) 적용 가산된 배당소득금액에 대해 종합소득세율을 적용하고, 세액공제로 11%에 해당하는 세액을 공제받음
세액공제 없음 배당가산액에 해당하는 세액(배당세액공제) 공제 가능
대표 예시 - 집합투자기구 배당소득
- 외국법인 배당소득
- 상장주식, 비상장주식 배당소득
- 국내법인 이익배당
목적 배당소득 자체만 과세 이중과세 조정(배당을 지급하는 법인이 이미 법인세를 납부했으므로, 이를 반영)
 

요약

✅ **배당가산(11% Gross-Up)**은 국내법인으로부터 받는 배당소득에 적용되어, 과세표준을 높이되, 이미 법인세가 납부되었다는 점을 고려해 배당세액공제를 해 줍니다.
배당가산하지 않는 경우에는 실제 배당금액만 과세표준에 반영되어 세액공제는 없습니다.

해외주식 배당은우 배당가산하지 않는 배당소득(T)이고

국내주식 배당은 배당가산하는 배당소득(G) 라고 볼수 있습니다.

내역이 누락되어서 추가가 필요한 경우 [배당소득 명세 추가]를 클릭합니다.

참고로 저는 금융소득명세서 와 홈택스 내용이 일치하여 추가 입력은 하지 않았습니다.
근로소득과 금융(이자.배당)소득이 입력이 완료되었으면 [작성완료]를 클릭합니다.

9) (이월)결손금

아래와 같이 팝업이 떠서 저는 예를 입력하였습니다.

저는 근로소득만 있는 경우여서 입력하지 않고 예를 눌렀습니다.

이는 개인에 따라 다를 수 있습니다.

10) 소득공제금액

근로소득 불러오기 창이 뜨면 공제 선택 체크 후에 저장하기를 클릭합니다.

 

소득공제금액 확인 후 [작성완료]를 클릭합니다.

11) 세액 공제 및 감면 금액 입력

세액 공제 및 감면 금액이 있는 경우 입력을 하고 [작성완료]를 클릭합니다.

 

기납부 세액은 국세청에서 보유한 자료 가져오기 클릭 시 자동으로 이자소득, 배당소득에 대해 징수액이 표시됩니다.

저는 해당 내용 확인 후에 [작성완료] 클릭하였습니다.

 

환급할 세액을 클릭합니다. 

종합소득세가 마이너스이면 환급이 된다는 말입니다.

즉, 국가에서 제 통장으로 돈을 쏴~ 준다는 말입니다.

받아야되는 금액보다 더 납부했다는 의미입니다.

감사합니다.~ 라는 마음으로 나의 환급계좌에 은행 및 계좌 번호를 적고 [작성 완료]를 클릭합니다.

 

저는 [작성완료] 클릭 시에 다음과 같은 오류 팝업이 떴습니다.

세액의계산_세액감면을 점검하라고 하네요.

이것은 무슨~ 안드로메다 언어인가요?

 

팝업 내요을 잘 보니 좌측메뉴의 종합소득세액산출계산서를 클릭 후에 금용소득자용 화면에서 내용 확인 후에 작성 완료를 클릭하라고 하네요.

역쉬~~ 팝업을 뚫어지게 잘보면 길이 보이는 군요.

내용 확인 후 [작성완료]를 클릭하였습니다.

 

세액감면 대상이 아닌 경우 세액감면(면제) 신청서를 입력하지 않아도 됩니다.

라고 나오네요. 

없는 것 같아서 예를 클릭하였습니다.

이는 개인마다 다를 수 있습니다.

 

이번에는 세액공제 대상이 아닌 경우 세액공제신청서를 입력하지 않아도 됩니다. 라고 뜨네요.

없는 것 같아서 예를 클릭하였습니다.

이는 개인마다 다를 수 있습니다.

 

세액공제.감면금액이 나옵니다.

작성완료를 클릭하였습니다.

 

기부금 내역이 없는 경우 기부금 명세서를 입력하지 않아도 됩니다. 라고 뜨네요.

없는 것 같아서 예를 클릭하였습니다.

이는 개인마다 다를 수 있습니다.

위아 같이 에러 팝업 내용의 항목을 체크 후에 작성을 해주니 에러가 사라졌습니다.

12) 신고서 제출하기

13) 지방소득세 신고하기

신고서 제출하기로 모든 것이 끝난 것이 아닙니다.

지방소득세 신고하기는 따로 존재합니다.

지방소득세를 신고하지 않아서 납세 기한이 지나면 가산세가 붙으며 환급 대상인 경우 지방소득세 신고하기를 하지 않으면 환급을 해주지 않습니다.

헐~~ 대박이죠.

반드시~~ 지방소득세도 신고해야합니다.

마치~~ 던전 속에 숨겨진 함정같습니다.

개인이 실수하면 국가는 개이득입니다.

[신고이동]을 클릭하면 wetax.go.kr 로 이동이 됩니다.

신고인 내용을 입력 후에 [다음]을 클릭합니다.

신고서 기본 정보, 신고세액 등도 자동 입력되어 있으니 확인 후 다음을 클릭합니다.

신고서제출에서도 [다음]을 누르면 안됩니다.

제가 홈택스에서 환급계좌를 입력했지만 이 것은 연동이 되지 않았네요.

아마도 일부러~ 일수도 기술적으로 어려워일 수도 있겠네요.

다시 입력해야 합니다. 이것이 숨겨 놓은 지뢰~ 입니다.

 

[제출]을 클릭하면 아래와 같이 신고서 제출이 완료되었습니다. 라고 뜹니다.

저는 이번에는 아주 작지만 환급되는 금액이 존재하더라구요.

[즉시납부] 버튼이 있어서 누르면 바로 통장으로 쏴~ 주는 줄 알았어요.

오~~ 탱큐 베리 투 머치하면서 [즉시납부]를 클릭했습니다.

 

클릭하니 납부할 수 없는 고지내역이 존재합니다.

라고 뜨네요.

인터넷을 뒤져보니 (정확히는 ChatGPT에게 물어봄)

종합소득세 환급금은 일반적으로 매년 5월 31일 신고 마감일을 기준으로 약 30일 이내에 지급됩니다. 즉, 대부분의 경우 6월 말에서 7월 초 사이에 환급금이 입금됩니다 

또한, 종합소득세 환급 후에는 지방소득세 환급이 이루어지는데, 이는 보통 7월 말에서 8월 사이에 지급됩니다.

라고 하네요.

아하~~ 낼 때는 빠르게~~ 돌려줄 때는 느리게~~ 군요.

그래도 돌려주신다는데 그정도는 참아야죠.

감사합니다. 행님~

 

14) 지방소득세 신고 내역 조회

wetax.go.kr 에 로그인합니다.

신고 > 종합소득분 > 종합소득분 신고내역을 클릭합니다.

확정 신고 내역이 다음과 같이 조회가 되고 진행상태가 신고완료로 나오면됩니다.

그리고 납부여부의 경우 납부할 금액이 있어서 납부를 했으면 납부완료 라고 나와야 하며

저 처럼 약간의 금액이지만 환급할 금액이 있어서 계좌 번호를 등록한 경우에는 납부여부가 빈칸으로 나옵니다.

나중에 환급이 되면 환급 이라고 표시되지 않을까 생각이 됩니다.

15) 홈택스 환급 계좌 신설

신고서 제출 시 환급 계좌를 등록할 수 있습니다.

그리고 홈택스에서 환급 계좌를 따로 등록이 가능합니다.

신고서 제출 시에 계좌 번호를 적었는지 기억이 가물~ 하다면 환급 계좌를 등록하면 좋을 것 같습니다.

환급은 찾아먹는 것이지 알아서 챙겨주지 않습니다.

오히려 개인이 환급을 받아가지 않아서 환급금이 소멸되면 국가 재정이 늘어나기 때문입니다.

하하~~ 이는 지극히 개인적인 생각이예요~~ 

암튼~~ 환급금 지급 적용 순서는 다음과 같습니다.

신고서에 기재한 계좌
개별 세목 환급계좌개설 신고한 계좌
모든 세목 환급계좌개설 신고한 계좌

홈택스 > 납부 고지 환급 > 국세 환급 > 환급계좌 신고/변경 신고

 

세목은 모든세목 또는 종합소득세를 선택합니다.

그리고 개설은행과 계좌 번호를 입력하면 됩니다.

등록이 완료되면 아래와 같이 계좌정보가 나옵니다.

그런데 계좌번호는 일부가 * 처리되어 있어서 전체 계좌번호가 무엇인지 확인이 불가능합니다.

나의 완벽주의자이다~ 다시 한번 확인하고 싶다고 생각이 되면 [변경 및 해지]를 누른 후 다시 확인하고 변경하면 됩니다.

그런데 환급 계좌를 등록하면서 계좌번호의 소유자 이름도 출력해서 내가 정확하게 입력해주었는지 한번더 표시를 해주면 얼마나~ 좋을까하는 생각을 해봅니다.

세상을 살면서 느끼는 것이지만 틀리면 정보 제공자가 유익한 것은 정확한 정보를 알려주지 않는 경향이 많은 것 같아요.

인터넷 가입을 할 때는 고객센터에서 가입을 선택하면 금방 상담원 연결이되는데 해약을 선택하면 전화연결이 엄청 안되잖아요.

16) 위택스 환급 계좌 신설

위택스 > 환급 > 환급계좌 신고

 

환급 계좌 등록하기 전체 환급계좌 조회를 선택하여 환급계좌가 있는지 체크 후에 없으면 환급계좌를 등록하면 됩니다.

환급 계좌가 없으면 등록을 합니다.

납세자와 신고인이 동일하면 납세자 우측의 신고인과동일을 체크합니다.

관할자치단체 선택 후에 계좌를 등록합니다.

하하~~ 길고도 긴 세금 신고가 끝났네요.

오죽하면 아인슈타인도 "세상에서 제일 이해하기 어려운것은 소득세이다."라고 하셨겠어요.

이는 바꾸어말하면 소득세 신고는 매우 어렵다~ 지뢰가 많다~ 지뢰 마다 가산세 및 과징금이 있다라는 의미로 지극히~ 개인적으로 해석해봅니다.

그나마 위안인 것은 우리나라는 연말정산도 그렇고 소득세 신고도 전산화가 잘되어 개인도 충분히 할수 있는 조건이 되었다고 생각합니다.

정말~ 살기 좋은 우리나라입니다.

아인슈타인 행님~~ 우리나라는 소득세 신고 쉬워요~~~

Korea Income tax 신고 is 거의 Full Auto~  몇개만 Land Mine~ 입니다요. 존경합니다요~ 행님 ^O^

 

Posted by 제이브레인
,

제가 키움증권을 사용하는데 해외 주식 양도 소득세를 내야 된다는 알림을 받았습니다.

작년에 국내 주식을 했는데 하는 종목마다 지뢰를 밟고 마이너스 행진이 계속되어 미국주식으로 넘어갔습니다.

미국 주식으로 테슬라, 마이크론 테크놀러지와 같은 대형주 위주로 했습니다.

미국 주식은 그래도 국내 주식 처럼 꾸준한~~ 우하향은 아니더군요.

그리고 갑자기 발생하는 뜬금포로 뜨는 오너 리스크나 유상 증자와 같은 악제가 상대적으로 적었습니다.

상대적으로 배당도 많이 주구요. 그런데 문제는 세금을 많이 낸다는 단점이 있기는 합니다.

미국 주식 + 해외 주식 소득을 합치면 마이너스 입니다.

그런데 원래 세금이라는 것은 가장 많이 내게 하는 방식인 것 같아요.

국내 와 해외로 나누고 배당, 이자소득, 매수 매도로 생긴 이익을 세부적으로 나눠서 전체를 나눈 후에 일부는 합치기는 하지만 대부분 따로 따로 과세를 하는 방식입니다.

즉, 전체가 마이너스여도 일부가 이익이 발생하면 세금을 내는 방식이죠..

그래서 저처럼 합계는 마이너스이지만 세금은 내는 그런 사람들이 있는 것이죠.

암튼~~ 억울한 마음은 많이 들지만 국가가 세금을 잘 써주기를 바라는 마음으로 납부를 하기로 했습니다.

1) 해외 주식 양도세 와 종합 소득세 차이점

해외 주식 양도세와 종합 소득세의 차이점을 정리해보았습니다.

2025년은 **5월 31일(토)**이 주말이고, **6월 1일(일)**도 휴일이기 때문에 **종합소득세 및 해외주식 양도소득세 신고 마감일이 2025년 6월 2일(월)**로 연장됩니다.

💡 해외 주식 양도소득세 vs 종합소득세 (이자·배당 포함) — 2025년 기준

항목 해외 주식 양도소득세 종합소득세 (배당·이자소득 포함)
과세 대상 해외 주식 매도 차익 배당소득, 이자소득, 근로·사업 등 종합소득
과세 방식 분리과세 (기타소득으로 분류) 종합과세 (다른 소득과 합산하여 누진세율 적용)
세율 연 250만원 공제 후 22% (지방세 포함) 원천징수 15.4%, 종합과세 선택 시 6.6%~49.5% 누진세율 적용
공제 연 250만원까지 양도차익 비과세 금융소득(이자+배당)이 2,000만원 이하일 경우 분리과세로 종결 가능
신고 시기 다음 해 5월 1일 ~ 2025년 6월 2일(월) 다음 해 5월 1일 ~ 2025년 6월 2일(월)
신고 방식 직접 신고 또는 증권사 대행 신고 가능 (예: 키움증권) 금융소득종합과세 대상자만 직접 신고 필요
신고 필요 여부 매도 차익 발생 시 자진신고 필요 (단, 키움 등에서 대행 가능) 금융소득(이자+배당)이 연 2,000만원 초과 시 자진신고
납부 방식 자진 신고 및 납부 (또는 대행 납부) 자진 신고 및 납부 (기본은 원천징수로 납부 완료)
예시 미국 주식 매도 차익 발생 → 키움 통해 신고 대행 가능 미국 주식 배당금, 국내외 예금이자 등 수령
 

2) 해외 주식 양도 소득세 대행 신고

키움증권에서 해외 주식 양도 소득세 대행 신고를 하면 된다고 하여 대행 신고를 했습니다.

키움증권 홈페이지, HTS, MTS 등 다양한 방법으로 신청이 가능하다고 합니다.

1) PC홈페이지 > 뱅킹/업무> 서류발급/조회 > 해외주식 양도세 대행신고 > 당사신청 / 타사신청

▶ http://www.kiwoom.com/h/banking/certificate/VFostockTransTaxView

2) 영웅문S# > 해외주식 > 양도세 > 당사신청

3) 영웅문S# > 해외주식 > 양도세 > 타사신청

 

저는 키움증권 홈페이지를 통해 신청했습니다.

홈페이지 상단의 전체 메뉴를 클릭합니다.

 

전체메뉴 > 뱅킹/업무 > 서류발급/조회/취소 > 

 

해외주식 양도소득세 대행 신고를 버튼을 클릭하면 됩니다.

카카오톡으로 온 해외주식 양도소득세 신고 알림입니다.

퍼스트원 세무법인 이라는 곳에서 보내주더군요. 

키움증권 세무관련으로 처리를 해주는 법인으로 보입니다.

내용을 보면 5월 10일까지 국세청 신고를 하고 5월 15일까지 신고서, 납부 이메일을 보내준다고 하네요.

2025년 이므로 아마도 년도에 따라 조금씩 다를 수 있을 것 같아요.

3) 대행 신고 후 기다리기

해외주식 양도소득세 대행 신고 후에 바로 납부가 가능한 것은 아닙니다.

위 알림톡 내용과 같이 2025년 5월 15일까지 납부 완료 이메일을 보내 준다고 합니다.

그래서 납부 완료 이메일을 기다렸습니다.

5월 15일은 아니고 20일에 알림톡이 왔습니다.

그런데 웬~걸 이메일이 오지 않았어요.

스팸으로 빠졌나 확인해보았는데 없었고 23일까지 기다렸지만 오지 않았어요.

제가 이메일 필터링을 많이 걸어놔서 이메일이 어디로 빠졌는지도 모르겠습니다.

키움증권 고객센터 1544-9000 번으로 전화를 하여 상담사와 연결을 시도 했지만 20분넘게 기다려도 전화 연결이 되지 않았습니다.

"지금 모든 직원이 통화 중이오니 잠시만 기다려 주십시요."

"대기인원 수는 30명 이상입니다."

"ARS 를 이용하려면 1번 직원 연결은 2번...."

위와 같이 대기만 타네요. 몇 날을 시도해보았지만 결과를 같았어요.

아마도 양도 소득세 및 종합소득세 관련으로 세금 납부가 있는 달이기 때문에 문의 사항이 많은 것 같아요.

암튼~ 상담사 전화만 시도할 수는 없어서 인터넷을 뒤져보니 대행 신고가 제대로 되면 홈택스에 결과가 나온다고 하더군요.

그래서 홈택스에 대행 신고가 되었는지 확인해보기로 했습니다.

4) 홈택스에서 대행신고 내역 확인

홈택스에 접속 후 로그인 합니다.

납부/고지/환급 > 세금납부 > 납부할 세액 조회/납부를 클립합니다.

 

저는 다음과 같이 과세구분은 정기신고로 제목은 양도소득세로 나왔어요.

 

5) 양도소득세 납부하기

하단을 보면 내역 아래에 납부하기 버튼이 있습니다.

먼저 납부할 양도소득세를 체크합니다. 그리고 하단의 [납부하기]를 클릭하여 납부하면 됩니다.

 

[납부하기]를 클릭하니 다음과 같은 팝업이 떴습니다.

지방소득세는 별도로 신고 납부 해야 한다고 하네요.

[닫기]를 클릭하면 국세 인터넷 납부 창이 뜹니다.

 

결제수단선택을 보면 계좌이체, 신용카드, 간편결제(신용카드)가 가능하군요.

저는 계좌이체를 선택하여 결제했습니다.

 

계좌이체를 선택하면 은행, 계좌번호, 계좌비밀번호, 전화번호를 넣으라는 팝업이 뜨며 해당 내용을 입력하고 납부하기를 클릭하면 아래와 같이 즉시 해당 은행에서 양도소득세가 빠져 나갑니다.

오~~ 신기하네요.

 

6) 지방소득세 납부하기

납부시 안내 팝업창에 있던 www.wetax.go.kr  에 접속합니다.

 

wetax 접속 시 아래와 같이 뜹니다.

양도소득분 신고.납부하기를 클릭합니다.

 

인증을 하라는 메뉴가 나오는데요.

여기서 본인의 인증서를 선택하고 클릭하면 회원가입하라고 나오네요.

회원 가입을 합니다.

 

회원 가입후 wetax.go.kr 에 접속한 화면입니다.

나의위택스를 클릭합니다.

 

[나의위택스] 선택 시 아래 팝업에 [나의위택스]가 있습니다.

선택하고 클릭합니다.

나의 위택스를 선택하면 아래와 같이 지방세 납부할 내역이 나옵니다.

금액은 해외주식 양도소득세의 10% 금액입니다.

예를 들면 해외주식 양도소득세가 100만원 이면 지방세는 10만원이네요. (2025년 기준)

그런데 문득~~ 한번에 결제하지 않고 따로 홈페이지도 다르고 다르게 납부하는지 궁금했습니다.

솔직히 잠깐 깜박하면 지방세 납부를 까먹을 수가 있는데 그런 경우 가산금(연체료)를 내잖아요.

조금 불편한 느낌이 들기는 했습니다.

확인을 해보니 다음과 같은 이유가 있었네요.

해외주식 양도소득세와 지방세를 각각 홈택스(국세)와 wetax(지방세)에서 따로 납부하는 이유는 바로 이 둘이 과세 주체가 다르기 때문이에요.

💡 과세 주체와 세목의 차이

  1. 양도소득세 (국세)
    • 해외주식 양도차익에 대한 세금은 국세로, 중앙정부(국세청)가 징수하는 세금이에요.
    • 홈택스는 국세청의 시스템이라서, 양도소득세를 신고하고 납부할 때 홈택스를 이용해요.
  2. 지방소득세 (지방세)
    • 지방소득세는 양도소득세의 일정 비율(보통 10%)을 지방정부가 징수해요.
    • 이건 지방세라서, 지방자치단체에서 별도로 관리해요.
    • 그래서 wetax(위택스)라는 지방세 포털에서 지방소득세를 납부해야 해요.

🔎 왜 둘로 나뉘었을까?

과거에는 지방소득세가 없었고, 국세만 냈어요. 하지만 지방자치단체의 재정 자립을 돕기 위해 지방세를 신설했어요. 그래서 현재는:

  • 국세 (홈택스): 중앙정부 재정
  • 지방세 (wetax): 지방자치단체 재정

이렇게 각각의 재정으로 운영되는 걸로 나눠서 징수하게 됐죠.

 

자동납부, 전자송달 신청하면 공제받을 수 있다고 하여 등록하러가기를 클릭했습니다.

그런데 주말에 클릭했더니 아래와 같이 나오네요.

 

 

평일에 다시 한번 자동 납부 신청을 해봐야겠네요.

지방소득세(지방세)의 납부 기한이 지나면 가산금(연체료 같은 개념)이 붙는데, 그 계산 방식은 아래와 같아요.


💡 지방소득세 가산금 계산

1️⃣ 가산금(연체료)

  • 납부기한이 지나면, **납부하지 않은 금액의 3%**가 일괄적으로 가산금으로 부과돼요.
  • 예를 들어, 납부하지 않은 세금이 100만 원이라면,
    ➜ 가산금 = 100만 원 × 3% = 3만 원이에요.

2️⃣ 중가산금(매월 추가되는 가산금)

  • 납부 기한이 지나고 1개월이 경과하면, 매달 0.75%씩 추가 가산금이 부과돼요.
  • 예를 들어, 100만 원을 1개월 넘게 연체하면
    ➜ 100만 원 × 0.75% = 7,500원이 한 달마다 추가돼요.

✅ 정리


항목 내용
기본 가산금 3% (납부 기한 경과 시, 일괄적으로 부과)
중가산금 0.75%씩, 매월 부과
 

따라서, 지방소득세를 빨리 납부할수록 불필요한 가산금을 피할 수 있어요.

생각보다 가산금이 쎄요~ 연체를 많이 하면 배보다 배꼽이 클수 있겠어요.

 

7) 환급 계좌 등록

자동납부는 주말이라 등록이 되지 않았고 환계좌는 등록이 되네요.

 

환급계좌 관련 신고인과 납세자를 등록합니다.

저는 신고인과 납세자가 같아서 신고인과 동일을 체크하였습니다.

그리고 환급계좌 조회에서 관할자치단를 선택 후에 검색을 클릭합니다.

 

저는 등록된 환급 계좌가 존재하지 않습니다. 라고 나왔습니다.

그래서 개설신고를 클릭하였습니다.

그리고 지방세, 세외 수입을 선택하고 은행 선택 후에 정보를 입력하였습니다.

은행선택하고 이미지로 된 번호 입력하고 계좌번호를 입력후에 [검증]을 클릭하면 검증결과에 검증이 성공하면 검증성공이라고 뜹니다.

저는 이상태로 등록완료가 된 줄 알았어요.

그런데 환급계좌를 조회를 하니 아무것도 뜨지 않는거예요~~

뭔가~ 쎄한 느낌이 들어서 다시 환급계좌정보를 입력 후에 화면을 뚤어져라~~~ 다시 보니

[제출] 버튼이 저 아래 꽁꽁 ~~ 숨겨져 있는거지 뭡니다.

이런~~ 이따구로~~ UI 디자인을 했나 하는 생각이 들었어요.

페이지 내용이나 사용 편의성에 만족하십니까?

항목에 매우불만족을 표시하고 싶은 마음은 굴뚝같았지만 저는 튀는 것은 좋아하지 않는 성격이고 저빼고 다른 사람들은 그런가부다~~ 하고 느낄수도 있는 생각이 들기도 하여 매우 만족을 클릭 후에 [제출]을 클릭했습니다.

 

환급계좌 신고가 정상적으로 되면 아래와 같이 개설신청이 정상적으로 완료되었습니다.

라고 뜹니다.

 

나의 위택스에 다시 들어가보면 다음과 같이 환급계좌에 등록정보가 나옵니다.

 

8) 자동납부 등록

자동납부 하단의 [등록하러가기]를 클릭합니다.

 

자동납부 신청 페이지에서 신고 구분 중 신규를 선택합니다.

 

관할자치단체를 선택 후 추가를 클릭합니다.

 

자동납부 신청 세목을 체크하고

동의합니다 체크 후

과세구분을 선택합니다.

저는 정기분+수시분 을 체크하였습니다.

최종적으로 선택을 클릭합니다.

 

납부방법을 입력합니다.

저는 계좌이체를 선택하였으며 납기일도 체크하였습니다.

"동의합니다" 체크 후에 제출을 클릭합니다.

 

9) 양도소득세 지방세 납부하기

자동납부 신청을 하면 자동 납부가 되지만 납부 기한이 있기 때문에 저는 해외주식 양도 소득세에 대한 지방세는 바로 납부하기로 하였습니다.

저는 국가를 사랑하는 성실한 납세자니까요~~  

(사실 지방세가 납부 기한에 납부되지 않으면 어쩌나 하는 걱정이 컸어요)

지방세에서 건수나 금액을 클릭합니다.

납부할 지방소득세를 체크후에 하단의 [납부]를 클릭합니다.

 

회원납부 또는 타인납부를 선택하라고 하네요.

저는 제 지방소득세 납부 이므로 회원납부를 선택하였습니다.

 

giro.or.kr 로 이동이 됩니다.

계좌이체, 신용카드, 간편결제 선택이 가능합니다.

저는 계좌이체를 선택하고 납부하였습니다.

납부 후에 다시 [나의 위택스] 에 들어가면 아래와 같이 지방세 1건 -> 0건으로 변경되었습니다.

10) 양도소득세 지방세 납부내역 조회

wetax.go.kr 에 로그인합니다.

신청 > 양도소득분 > 양도소득분 신고내역 을 선택합니다.

양도소득분 확정신고내역을 확인해보면 아래와 같이 검색이 되어야 하며 진행상태는 신고완료로 나와야 합니다.

저는 납부할 금액이 있어서 납부를 완료했으며 납부여부에 납부라고 나옵니다.

어찌 보면 쉽고 어찌 보면 어려운 해외주식 양도소득세 납부를 했네요.

요약하면 홈택스에서 양도소득세 납부하고 위택스에서 지방세 납부하는 것입니다.

뭐든지~ 처음이 어렵지 두번째는 쉽습니다.

아는 것이 힘이다~ 아자~ 아자~~ 입니다.

그럼 즐거운 하루되세요~~~

 

Posted by 제이브레인
,

fabric 으로 원격 호스트에서 접속하고 로그를 가져올 때 연결되어 있는 호스트가 복잡하고 로그 path 로 여러개가 존재하면
단일 함수로 작성하는 것이 힘들고 함수 자체고 너무 커지고 복잡해져서 디버깅이 힘듭니다.
그리고 로그를 가져온 후에 로그를 원하는 파트로 분해하는 parsing 과정이 필요합니다.

즉, 호스트가 매우 많고 path 도 여러개이고 parsing 도 복잡한 과정이 필요하면 각각에 많는 모듈로 분해를 할 필요가 있습니다.
호스트 몇개 없을 때는 문제가 발생하지 않지만 호스트가 매우 많다면 각각을 담담할 모듈로 분해를 해야합니다.

모듈 설명
SshManager ssh connection 연결을 담당
LogSearchScheduler 로그 검색을 Step 별로 나누어 각 Step 별 검색 스케줄링을 담당
LogParser  로그 파싱을 담당
LogSearchManager 그 검색의 메인 역할을 담당하고 각 모듈을 통합


1) SshManager

Python의 FabricSSH 연결을 기반으로 동작합니다.

Fabric은 원격 서버에 SSH로 접속해서 명령을 실행하거나 파일을 업로드/다운로드하는 작업을 간편하게 자동화할 수 있는 라이브러리입니다. 주로 배포 자동화서버 관리 등의 작업에 많이 사용됩니다.

핵심은 paramiko를 사용해 SSH 연결을 시도하는 것이며, 맞는 비밀번호를 찾으면 해당 정보를 반환합니다.

아래 코드는 git 을 통해 다운받을 수 있습니다.

git clone git@github.com:jbpark/jbDeskExample.git
cd jbDeskExample/jbDesk/ch3.3

 

ssh_util.py

import logging

import paramiko

from lib.util.encoding_util import decrypt_cipher_text

# logging.basicConfig(level=logging.DEBUG)

def get_ssh_user_info(ssh_user_infos, host_ip, first_index, check_sudo=True):
    attempts = 0  # Counter for keeping track of attempts

    sorted_infos = sorted(ssh_user_infos, key=lambda x: (x.index != first_index, x.index))

    for ssh_connect_info in sorted_infos:
        user_name = ssh_connect_info.user_name
        password = decrypt_cipher_text(ssh_connect_info.password)
        print(f"Attempting attempts:{attempts}, index:{ssh_connect_info.index}")

        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        try:
            client.connect(host_ip, username=user_name, password=password, timeout=2)
            print(f"[>] Valid password found index:{ssh_connect_info.index}")
            client.close()
            return ssh_connect_info
        except paramiko.AuthenticationException:
            print("Invalid password!")
        except Exception as e:
            print(f"Connection failed: {e}")
        finally:
            client.close()

        attempts += 1  # Increment the attempts counter for each password

    return None

 

🔍 주요 기능 요약

  • ssh_user_infos: 여러 개의 SSH 사용자 정보를 가진 객체 리스트 (아마 사용자명, 암호화된 비밀번호, 우선순위 index 포함).
  • host_ip: 접속하려는 원격 서버의 IP 주소.
  • first_index: 우선적으로 시도할 계정 index.
  • decrypt_cipher_text: 암호화된 비밀번호를 복호화하는 함수.

✅ 동작 흐름

  1. first_index 기준으로 우선순위를 두어 정렬 (first_index를 먼저 시도).
  2. 각 사용자 정보로 SSH 접속 시도.
  3. 접속 성공하면 해당 사용자 정보를 반환.
  4. 실패하면 다음 사용자 정보로 계속 시도.
  5. 모두 실패하면 None 반환.

 

SSH 를 시도하는 계정 정보는 settings 폴더 하위에 xxx.yaml 파일로 저장하면 됩니다.

SshManager 는 fabric 으로 로그를 가져 오기 전에 해당 호스트에 접속 가능한 계정 정보를 찾습니다.

이 때 yaml 파일에 등록된 계정 정보를 읽어서 사용합니다.

ssh.yaml

SSH:
  - user_name: root
    password: gAAAAABn9K2pfYmSQT0c4Ay4qenyD99ffiWxTQPDOtIa98j9H8WTZ0HpaXfXEvETFLP-Tz59UJVMMBIFw5LIjxBSjlCD4wSOLw==

  - user_name: vagrant
    password: gAAAAABn9K2pfYmSQT0c4Ay4qenyD99ffiWxTQPDOtIa98j9H8WTZ0HpaXfXEvETFLP-Tz59UJVMMBIFw5LIjxBSjlCD4wSOLw==

 

위 YAML 파일은 SSH 접속에 사용할 계정 정보 리스트를 담고 있습니다.

🧾 각 항목 설명:

필드 이름 설명
SSH 최상위 키로, SSH 관련 정보를 담는 리스트
user_name SSH 접속에 사용할 사용자명 (예: root, vagrant)
password 암호화된 비밀번호 문자열 (Fernet 로 암호화)

 

encoding_util.py

from cryptography.fernet import Fernet

from lib.models.constants.config_key import LDAP_KEY


def decrypt_cipher_text(ciphered_text):
    cipher_suite = Fernet(LDAP_KEY)
    try:
        result = str((cipher_suite.decrypt(ciphered_text.encode())), 'utf-8')
    except Exception as e:
        result = None
        print(f"An error occurred: {e}")

    return result


def encrypt_cipher_text(text):
    cipher_suite = Fernet(LDAP_KEY)
    try:
        result = str(cipher_suite.encrypt(text.encode()).decode())
    except Exception as e:
        result = None
        print(f"An error occurred: {e}")

    return result

이 코드는 cryptography 라이브러리의 Fernet 모듈을 이용하여 문자열을 암호화/복호화하는 유틸리티 함수들을 정의하고 있습니다. 주로 비밀번호, API 키, 인증 정보 같은 민감한 데이터를 안전하게 처리하기 위해 사용됩니다.


🔐 핵심 개념 요약

  • Fernet은 대칭키(하나의 키) 기반 암호화 방식입니다.
  • LDAP_KEY는 암호화와 복호화에 사용되는 비밀 키입니다.
  • 암호화된 문자열은 일반적으로 gAAAAA... 형태로 시작합니다.

📌 요약

함수 이름 설명
encrypt_cipher_text 평문을 Fernet으로 암호화
decrypt_cipher_text 암호화된 문자열을 복호화
LDAP_KEY 암호화/복호화에 사용되는 비밀 키

 

2) LogSearchScheduler

주어진 조건에 따라 로그 검색 단계를 스케줄링하고 연결 정보를 준비하는 로직을 담고 있습니다. 이 클래스는 BaseLogSearchScheduler라는 부모 클래스를 상속받아 동작합니다.


📌 정리


항목 설명
LogSearchScheduler 로그 검색 단계별 작업을 스케줄링하는 클래스
get_step_connect_infos 주어진 단계에 맞는 SSH 등 연결 정보 설정
schedule_steps 서비스 유형에 따라 로그 검색 단계를 설정
사용 예 멀티 스텝 로그 분석 (예: Gateway → API → Echo 순으로 연결 확인 및 로그 추출)

 

다음은 로그 검색 스케줄링 시스템의 베이스 클래스, BaseLogSearchScheduler의 정의입니다.

base_log_search_scheduler.py

from lib.models.constants.const_response import RespStatus, RespMessage
from lib.models.log.respone.log_search_response import LogSearchResponse
from lib.util.config_util import load_service_connect_infos_from_yaml, load_ssh_user_infos_from_yaml


class BaseLogSearchScheduler:
    def __init__(self, manager, yaml_loader, config_loader):
        self.manager = manager
        self.yaml_loader = yaml_loader
        self.config_loader = config_loader
        self.env = manager.env
        self.keyword = manager.keyword
        self.service_name = manager.service_name
        self.level = manager.level
        self.logs = None
        self.index = 0
        self.total = 0

        # 전체 main steps
        self.all_main_steps = []

        # 현재 main step
        self.current_main_step = None

        # 전체 sub steps
        self.all_sub_steps = None

        # 현재 sub step
        self.current_sub_step = None

        self.step_connect_infos = None

        self.all_connect_infos = self.get_all_connect_infos()
        self.env_connect_infos = self.get_env_connect_infos()
        self.ssh_connect_infos = self.get_ssh_connect_infos()

    def get_all_connect_infos(self):
        if self.env is None:
            return None

        return load_service_connect_infos_from_yaml(self.yaml_loader)

    def get_env_connect_infos(self):
        env_connect_infos = []

        for item in self.all_connect_infos:
            if item.env.upper() != self.env.upper():
                continue

            env_connect_infos.append(item)

        return env_connect_infos

    def exist_main_step(self):
        if self.all_main_steps:
            return True
        else:
            return False

    def exist_sub_step(self):
        if self.all_sub_steps:
            return True
        else:
            return False

    # log search step 을 리턴함
    def get_current_main_step(self):
        return self.current_main_step
    
    # sub steps 를 스케줄링
    def schedule_sub_steps(self):
        print("schedule_sub_steps")

    def get_next_main_step(self):
        if not self.all_main_steps:
            return None

        self.current_main_step = self.all_main_steps.pop(0)
        return self.current_main_step

    def get_all_sub_steps(self):
        return self.all_sub_steps

    def get_next_sub_step(self):
        if not self.all_sub_steps:
            return None

        self.current_sub_step = self.all_sub_steps.pop(0)
        return self.current_sub_step

    # log search step 을 리턴함
    def get_step_connect_infos(self, step):
        return self.step_connect_infos

    def ensure_step_connect_info(self):
        print("")

    def get_step_log_path(self, step):
        return self.step_log_path

    def get_ssh_connect_infos(self):
        return load_ssh_user_infos_from_yaml(self.yaml_loader)

    def setLogs(self, logs):
        self.logs = logs

    def get_failed_response(self, message):
        response = LogSearchResponse()
        response.command_type = "log"
        response.status = RespStatus.FAILED.value
        response.message = message
        response.index = self.index
        response.total = self.total
        return response

    def get_success_response(self):
        response = LogSearchResponse()
        response.command_type = "log"
        response.status = RespStatus.SUCCESS.value
        response.message = RespMessage.SUCCESS.value
        response.index = self.index
        response.total = self.total
        return response

    def get_connect_infos_by_service_name(self, service_name):
        connect_infos = []

        for item in self.env_connect_infos:
            if item.service.service_name != service_name:
                continue

            connect_infos.append(item)

        return connect_infos

 

여러 서버 환경 및 서비스 구성 정보를 바탕으로 로그 검색 단계를 스케줄링하고, 연결 정보를 관리하는 핵심 역할을 합니다.


📦 주요 기능 정리

  설명
환경 설정 로딩 YAML에서 서비스 및 SSH 연결 정보 로드
단계 관리 로그 검색 단계(main/sub step) 관리
연결 정보 제공 특정 서비스/환경에 맞는 SSH 및 서비스 연결 정보 반환
응답 생성 성공/실패 응답(LogSearchResponse) 생성

 

3) LogParser 

로그 파싱은 정규식을 사용합니다.

이 코드는 로그 분석이나 정규식 기반의 파싱 시스템에서 정규표현식 패턴을 정의하는 상수 집합입니다. RegPattern 클래스는 다양한 로그 항목을 추출하기 위해 사용되는 파라미터화된 정규표현식 템플릿을 제공합니다. 즉, 특정 필드명을 지정하면 그것에 맞는 정규식을 생성할 수 있도록 설계되어 있습니다.

reg_pattern.py

from lib.models.log.log_pattern import LogPattern


class RegPattern:
    WORD = r'(?P<%s>\S+)'
    B_WORD = r'\[(?P<%s>\S+)\]'
    B_WORD_AST = r'\[(?P<%s>\S*)\]'
    TIME_RECEIVED = r'\[(?P<%s>[\w:/]+\s[+\-]\d{4})\]'
    TIMESTAMP = r'(?P<%s>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2},\d{1,3})'
    REQUEST_FIRST_LINE = r'"(?P<%s>(\S+) (\S+)\s*(\S+)\s*)"'
    STATUS = r'(?P<%s>\d{3})'
    USER_AGENT = r'"(?P<%s>(\S+)\s*(\S+))"'
    DQ_WORD = r'"(?P<%s>\S+)"'
    DIGIT_MS = r'(?P<%s>\d{1,}ms)'
    LEVEL = r'(?P<%s>(INFO|ERROR|WARN|TRACE|DEBUG|FATAL))'
    B_TRACE_SPAN_PARENT_ID = r'\[(?P<%s>[A-Za-z0-9]*,[A-Za-z0-9]*,[A-Za-z0-9]*)\]'
    ANY = r'(?P<%s>.*)'
    RESOURCE = r'resource=(?P<%s>\d{1,5}),'
    STATUS_CODE = r'"status_code":"(?P<%s>[A-Za-z0-9]*)"'
    REQUEST_ID = r'"request_id":"(?P<%s>[A-Za-z0-9]*)"'

    SPACE = LogPattern(r'\s', None)
    SPACE_MORE = LogPattern(r'\s+', None)
    COLON = LogPattern(r':', None)
    HYPHEN = LogPattern(r'-', None)

🔍 구조 설명

📦 클래스: RegPattern

이 클래스는 모두 클래스 변수(class-level constant)로 정의되어 있으며, 로그 문자열을 파싱하기 위해 활용됩니다.


📌 주요 패턴 설명

변수명설명정규표현식비고

 

변수명 설명 정규표현식 비고
WORD 공백 없는 단어 (?P<%s>\S+) 그룹 이름 지정 가능
B_WORD 대괄호로 둘러싸인 단어 \[(?P<%s>\S+)\] 예: [INFO]
B_WORD_AST 빈 문자열도 허용 \[(?P<%s>\S*)\] 빈 문자열도 가능
TIME_RECEIVED Apache 로그 시간 \[(?P<%s>[\w:/]+\s[+\-]\d{4})\] 예: [10/Oct/2000:13:55:36 -0700]
TIMESTAMP 일반적인 타임스탬프 (?P<%s>\d{4}-\d{2}-\d{2} ... ) 예: 2023-09-01 12:30:45,123
REQUEST_FIRST_LINE HTTP 요청 첫 줄 "(?P<%s>(\S+) (\S+)\s*(\S+)\s*)" 예: "GET /index.html HTTP/1.1"
STATUS HTTP 상태 코드 (?P<%s>\d{3}) 예: 200, 404
USER_AGENT User-Agent 파싱 "(?P<%s>(\S+)\s*(\S+))"  
DQ_WORD 큰따옴표로 둘러싸인 단어 "(?P<%s>\S+)"  
DIGIT_MS ms 단위 시간 (?P<%s>\d{1,}ms) 예: 300ms
LEVEL 로그 레벨 `(?P<%s>(INFO ERROR
B_TRACE_SPAN_PARENT_ID Trace 정보 \[(?P<%s>...) 예: [abc123,def456,ghi789]
ANY 아무 문자열 (?P<%s>.*)  
RESOURCE resource 정보 resource=(?P<%s>\d{1,5}),  
STATUS_CODE JSON 로그의 status_code "status_code":"(?P<%s>[A-Za-z0-9]*)"  
REQUEST_ID JSON 로그의 request_id "request_id":"(?P<%s>[A-Za-z0-9]*)"  

 

4) 코드 실행

Vagrant 파일은 3.2편에서 만든 vagrant 파일을 사용했습니다.

아래와 같이 gateway01, api01, echo01 VM 은 실행 중이어야 합니다.

 

jbdesk.py 를 실행합니다.

위 서버의 로그를 검색하기 위해서는 Tid 를 입력하고 Search 를 클릭하면 됩니다.

Posted by 제이브레인
,