728x90
반응형

JSON 파일 읽기

  • json 파일은 /assets/front 디렉토리 아래에 위치한다.
  • 파일은 한국어 번역 파일인 ko.json 과 en.json으로 구성됨
  • json 형식의 번역 파일은 프론트에서 다국어 처리를 위해 사용된다.
func ReadJson(fileName string) ([]byte, error) {
    // 현재 프로젝트 루트 디렉토리
    pwDir, getPwdErr := os.Getwd()

    // 에러 발생 시, 에러 리턴. 에러 발생 파일은 스킵할 수 있게 하기 위하여 리턴함
    if getPwdErr != nil {
        log.Printf("[READ_JSON] Get Pwd Error: %v", getPwdErr)
        return []byte{}, getPwdErr
    }

    file, readErr := os.ReadFile(pwDir + "/assets/front" + fileName)

    if readErr != nil {
        log.Printf("[READ_JSON] Read File Error\nFileName: %s, Error: %v", fileName, getPwdErr)
        return []byte{}, readErr
    }

    return file, nil
}

JSON 데이터 파싱

  • 파일명에 따라 한국어 / 영어로 구분
  • json 파일의 형식은 '{First: {Second: { Third: {"단어": "번역내용"}}}}' 의 형식으로 되어있다.
    • Third 에서 depth가 더 들어가는 케이스도 있음
type JsonRowStruct struct {
    First string
    Second string
    Third string
    Fourth string
}

func ParseJsonData(jsonData []byte) (map[string]interface{}, error) {
    // json 파일 데이터 매핑할 맵 변수 선언 -> 키값 아래에 데이터를 매핑시키는 방식으로 관리
    var jsonDataMap map[string]interface{}

    // json 데이터를 위에서 선언한 Map에 넣어주기
    unMarshalErr := json.Unmarshal(jsonData, &jsonDataMap)

    if unMarshalErr != nil {
        log.Printf("[PARSE_JSON] unmarshal json error: %v", unMarshalErr)
        return map[string]interface{}, unMarshalErr
    }

    return jsonDataMap, nil
}

파싱된 JSON 데이터 평탄화

  • 이러한 Depth 들을 평탄화 시켜줘야 액셀 파일에 넣기 용이함
    • 평탄화 하지 않으면 depth 별로 for 문을 돌려야 하고, depth 길이에 따른 동적 처리가 어려워 졌었음
// 액셀 파일 데이터 구조체
type FileWork struct {
    file        *excelize.File
    FileName    string
    SheetLength int
}

// 이미 한글/영어 데이터가 입력 되었을 때, 코드 값을 기준으로 데이터를 입력할 위치 탐색
func (excelData *FileWork)FindCode(sheetName string, rowIndex int, majorDepth , secondDepth, thirdDepth, fourthDepth string) bool {
    // 각 깊이별로 셀 값을 가져옴
    cellFirst, _ := excelData.file.GetCellValue(sheetName, fmt.Sprintf("A%d", rowIndex))
    cellSecond, _ := excelData.file.GetCellValue(sheetName, fmt.Sprintf("B%d", rowIndex))
    cellThird, _ := excelData.file.GetCellValue(sheetName, fmt.Sprintf("C%d", rowIndex))
    cellFourth, _ := excelData.file.GetCellValue(sheetName, fmt.Sprintf("D%d", rowIndex))

    // 데이터 코드 비교
    if cellFirst == majorDepth 
      && cellSecond == secondDepth 
      && cellThird == thirdDepth 
      && cellFourth == fourthDepth {
          return true
      }

        return false
}


// 데이터 평탄화
func (excelData *FileWork)FlattenJsonData(sheetName string, parsedData map[string]interface{}) {
    switch v := data.(type) {
    case map[string]interface{}:
        for key, value := range v {
            newPrefix := prefix

            if prefix != "" {
                newPrefix += "."
            }

            newPrefix += key

            // map value의 데이터 역시 depth가 있을 수 있기 때문에 평탄화 시킨다
            excelData.flattenKorJSON(sheetName, value)
        }
     // 문자로 들어왔으면 카테고리 구분하여 처리 - 이미 front 시트에 데이터가 들어가 있을 때
    case string:
        depths := strings.Split(prefix, ".")
        firstDepth := ""
        secondDepth := ""
        thirdDepth := ""
        fourthDepth := ""

        if len(depths) > 0 {
            firstDepth = depths[0]
        }
        if len(depths) > 1 {
            secondDepth = depths[1]
        }
        if len(depths) > 2 {
            thirdDepth = depths[2]
        }
        if len(depths) > 3 {
            fourthDepth = strings.Join(depths[3:], ".")
        }

        // 엑셀의 모든 행을 탐색하여 코드 일치 여부 확인
        rows, err := excelData.file.GetRows(sheetName)
        if err != nil {
            log.Printf("GetRows Error: %v", err)
            return
        }

        for rowIndex := range rows {
            // 첫 번째 행은 헤더이므로 건너뜀
            if rowIndex == 0 {
                continue
            }

            // 1번째 row는 칼럼 값들이므로, 2번째 줄 부터 데이터 입력(index+1)
            isCodeMatch := excelData.FindCode(sheetName, rowIndex+1, firstDepth, secondDepth, thirdDepth, fourthDepth)

            if isCodeMatch {
                // 'F' 열에 값 삽입
                excelData.file.SetCellValue(sheetName, fmt.Sprintf("E%d", rowIndex+1), v)
                break // 매칭되는 첫 번째 행에만 값을 삽입하고 루프 종료
            }
        }

    default:
        // 다른 타입은 처리하지 않음
    }
}

액셀 파일 생성 및 데이터 저장

  • 데이터를 처리, 액셀 파일 저장하기 위해 github.com/xuri/excelize/v2 외부 패키지를 사용하였다.
  • 매핑된 데이터를 평탄화하여 저장한다.
# 패키지 설치
go get -u github.com/xuri/excelize/v2

// 번역 엑셀 파일 존재 여부 체크
func (excelData *FileWork)CheckIfFileExist() bool {
    _, err := os.Stat(excelData.fileName)

    return !os.IsNotExist(err)
}

// 파일 저장
func (excelData *FileWork) SaveExcelFile() {
    savingErr := excelData.file.SaveAs(excelData.FileName)
    if savingErr != nil {
        log.Printf("[EXCEL_SAVE] Saving File Error: %v", savingErr)
    } else {
        log.Printf("[EXCEL_SAVE] Saving Success: %s", excelData.file.FileName)
    }
}

// 칼럼 설정
func (excelData *FileWork) SetJsonColumns() {
    excelData.file.SetCellValue(sheetName, "A1", "First")
    excelData.file.SetCellValue(sheetName, "B1", "Second")
    excelData.file.SetCellValue(sheetName, "C1", "Third")
    excelData.file.SetCellValue(sheetName, "D1", "Ko")
    excelData.file.SetCellValue(sheetName, "F1", "En")
}

func (excelData *FileWork)WriteJsonExcelFile(parsedData map[string]interface[}) error {
    // 번역 파일 존재 여부에 따라 액셀 파일 여는 방식이 다르므로 미리 선언
    // 프로그램 실행 시 먼저 파일을 만들고, 항상 openFile 하는 방법으로 작성하였으나 여기서는 두가지 방식을 보여주기 위해 분리해서 보여줌    
    if CheckIfFileExist(excelData.fileName) {
        file, openErr := excelize.OpenFile(excelFilePath)

        if openErr != nil {
            log.Printf("[EXCEL_WRITE] Open Excel File for JSON Error: %v", openErr)
            // 파일 오픈 실패 시, 새로 파일 생성
            excelData.file = excelize.NewFile()
        }

        excelData.file = file
    } else {
        excelData.file = excelize.NewFile()
    }

    sheetIndex, indexErr := excelData.file.GetSheetIndex("front")

    if indexErr != nil {
        log.Printf("[EXCEL_WRITE] Failed to get Sheet Index for JSON error: %v", indexErr)
        return indexErr
    }

    excelData.SetJsonColumns()

    return nil
}
728x90
반응형
728x90
반응형
  • 본 내용은 과거 번역 파일 정리 자동화 프로그램 작업에 대한 업무 일지이다.

시작

  • 회사에서 서비스에 사용되는 다국어 데이터의 검수 및 정리를 위한 액셀 파일 필요
  • 수기로 작업하던 방식에 불만을 갖게 되어, 나도 모르게 자동화 프로그램을 만들어주겠다고 선언해 버렸다
  • 기능 정리를 해 보면 아래와 같다.
    • 번역 파일을 액셀파일로 만들고, 수정 뒤에 다시 액셀파일을 번역 파일 생성해 주는 프로그램 각각을 만들어야 할 것으로 판단하였음
    • 파일 뿐만 아니라, DB의 데이터도 처리해야 해야 했음
  • 복기하는 겸, 그 내용의 일지를 옮겨 적어본다.

액셀 파일 생성 프로그램 - 번역 파일 -> 하나의 액셀 파일

  • 다국어 데이터를 사용하는 서비스는 총 3개
  • 파일들은 백엔드에서 사용하는 파일 / 프론트에서 사용하는 파일 / DB 에서 사용하는 파일 각각은 front / back / DB 폴더 안에 위치
  • 프론트에서 사용하는 다국어는 json 파일로 관리되고, 백엔드에서는 .properties 파일과 DB로 관리됨
    • properties 파일: <단어> = <번역된 단어> 형태로 관리됨
      • 기능에 따라 폴더가 구분되어있음. 이 폴더명을 back_<폴더명> 명으로 사용
      • ko.properties, en.properties로 파일이 나뉘어져 있다.
      • 두 파일은 같은 key를 공유하고 있기 때문에, 하나의 시트에 합쳐서 보여질 수 있게 한다.
      • key, ko, en 으로 칼럼을 잡았다.
    • json 파일: {"key1": {"key2": {"단어": "번역내용"}}} 형태로 관리됨
      • key2 아래에 Depth 가 최대 두개까지 더 들어갈 수 있다.
      • 파일은 한국어 ko.json과 en.json 두가지로 나뉘어지고, 두 파일을 하나의 sheet 에서 보여주게끔 진행해야 함
      • sheet명은 front_json 으로 잡음
      • 따라서 First, Second, Thir, Fourth, Ko, En 을 칼럼으로 잡았다.
    • DB: Sequential 한 id 값이 Primary Key 이고, ko 필드에 한국어 / en 영어 데이터가 저장되어 있음
      • 프로그램 실행 시, DB 에서 데이터 조회
      • 테이블 명을 db_<테이블명> 명으로 잡음
      • id, ko, en 으로 칼럼명을 잡았다.

번역 파일 재생성 - 하나의 액셀 파일 -> 기존 폴더 구조에 맞게 번역 파일로 분리

  • 액셀 파일에 저장 및 수정이 되어 전달 받았을 때, 다시 번역 파일 생성 및 데이터 업데이트/삽입 프로그램
  • json, properties 파일과 DB 내용을 다시 업데이트 해 주기
    • sheet 명의 prefix에 따라, front / back / db 로직으로 분리
    • 다시 생성한 번역 파일들은 dist 폴더 아래에 저장
    • properties 파일 - dist폴더 아래에 prefix(back)으로 디렉토리 생성 후 그 아래에 파일 저장
      • 각각 시트명에 대한 폴더 생성
      • ko / en 칼럼들은 en.properties / ko.properties 파일로 분리 시켜 저장
      • <key칼럼> = <번역 내용> 으로 데이터 생성해서 저장
    • json 파일 - dist 폴더 아래에 prefix(front)으로 디렉터리 생성 후 그 아래에 파일 저장
      • ko 칼럼은 ko.json 파일로, en 칼럼은 en.json 파일로 저장
      • First, Second, Third, Fourth 칼럼들은 각각 json의 키깂으로 사용.
    • DB 데이터 - 해당 데이블에 데이터 업데이트 / 삽입
      • id 값을 PK 로 사용하고 있기 때문에, INSERT INTO ON DUPLICATE KEY 로 쿼리
      • 실 사용 서버에 적용시키기 전 개발 서버의 DB에 적용

설계

  • 언어는 Golang으로 선택. 실행 파일을 빌드해서 넘기기에 용이하며, 여러 OS 에서 실행가능함
  • 번역 파일들은 그 폴더 채로 assets 폴더 안에 옮겨두고 프로그램 실행하면 읽고 각 폴더명에 맞게 sheet 생성 및 데이터 저장
    • 폴더 없이 가장 상위 레벨 디렉토리의 파일은 default 라는 시트명 안에 저장
  • DB 데이터들은 데이터 조회 후, 테이블명을 sheet 명으로 지정하고 id - ko - en 형식으로 데이터 저장
  • 액셀 파일들은 하나의 파일 안에 모든 데이터가 저장되어야 함
  • 처리가 완료되면, 프로그램은 종료되고 실행 파일과 같은 디렉토리에 액샐파일 생성
728x90
반응형
728x90
반응형

취약점 공격

SQL Injection

  • 웹 응용 프로그램에 SQL 삽입하여, 서버 데이터 유출 및 관리자 인증 우회 방법
  • 동적 쿼리에 사용되는 입력 데이터에 예약어 및 특수만자 입력되지 않게 필터링시켜 방지
    • 동적 쿼리: 질의어 코드를 문자열 변수에 넣어 조건에 따라 질의를 동적으로 변경하여 처리

크로스사이트 스크립팅 XSS

  • 웹페이지에 악의적인 스크립트를 삽입하여 방문자들의 정보를 탈취 및 비정상적 기능 수행 유발
  • HTML 태그의 사용을 제한 및 스크립트에 삽입되지 않도록 다른 문자로 치환하여 방지

운영체제 명령어 삽입

  • 외부 입력값을 통해 시스템 명령어의 실행 유도하여 권한 탈취 및 시스템 장애 유발
  • 웹 인터페이스를 통해 시스템 명령어가 전달되지 않도록하고, 외부 입력값을 검증 없이 내부 명령어로 사용하지 않게끔 함

서비스 공격 유형

서비스 공격 (Dos; Denial of Service)

  • 서비스 자원을 고갈시킬 목적으로, 다수의 공격자/시스템에서 대량의 데이터를 한 곳의 서버에 집중적으로 전송

Ping of Death

  • Ping 명령을 전송할 때, 인터넷 프로토콜 허용 범위 이상으로 전송하여 네트워크 마비시키는 공격

스머핑 SMURFING

  • IP 나 ICMPO의 특성을 악용하여, 엄청난 양의 데이터를 한 사이트에 집중적으로 보냄으로써 네트워크를 불능 상태로 만드는 공격

DDoS

  • 여러 곳에 분산된 공격 지점에서 한 곳의 서버에 대해 서비스 거부 공격을 수행하는 것
  • 취약점 가진 호스트들에 공격용 툴을 설치해 에이전트로 만듬

네트워크 침해 관련 용어 정리

  • 스미싱: 문자 메세지를 이용해 정보 빼내는 기법
  • 스니핑: 네트워크의 중간에서 남의 패킷 정보를 도청하는 유형. 수동적 공격에 해당
  • ARP 스푸핑: 자신의 물리적 주소(MAC)을 공격 대상의 것으로 변조해서 공격 대상에 도달해야하는 데이터 패킷을 가로채거나 방해

정보 보안 침해 공격 관련 용어 정리

  • 웜: 네트워크를 통해 자신을 복제하여 시스템 부하를 높여 다운시키는 바이러스의 일종.
    • 분산 서비스 거부 공격, 버퍼 오버플로 공격, 슬래머 등
  • 랜섬웨어: 인터넷 사용자의 컴퓨터에 잠입해 내부 문서나 파일등을 암호화하여 돈 요구
  • 트로이목마: 정상적인 기능을 하는 프로그램으로 위장하여 숨어있다가 부작용 일으킴. 복제 기능 없음

취약점 방어

NULL 포인터 역참조

  • NULL Pointer가 가리키는 메모리의 위치에 값을 저장할 때 발생하는 보안 약점
  • 포인터 이용 전 NULL 값 존재 여부 체크 함으로 방지

스택가드

  • 주소가 저장되는 스택에서 발생하는 보안 약점을 막는 기술
  • 프로그램 복귀 주소와 변수 사이 특정 값을 저장한 후, 그 값이 변경되었을 경우 오버플로우 상태로 판단하여 실행 중단
728x90
반응형

'백엔드 Backend' 카테고리의 다른 글

[테스팅] 어플리케이션 테스팅  (2) 2024.11.01
[결합도/응집도] 결합도와 응집도란  (0) 2024.10.24
악성코드 종류  (0) 2024.10.16
SOLID 원칙  (0) 2024.10.08
비동기 Asynchronous 란?  (0) 2024.10.04
728x90
반응형

어플리케이션 테스트

  • 어플리케이션에 잠재된 결함을 찾아내는 일련의 행위 또는 절차
    • Verification 검증 -> 개발자 입장, 소프트웨어 명세서 만족
    • Validation 확인 -> 사용자 입장, 고객 요구사항 만족

어플리케이션 테스트 원리

  • 완벽한 테스트는 불가능
    • 잠재적 결함을 줄일 수 있지만, 결함이 없다고 증명할 수 없음
  • 파레토 법칙
    • 어플리케이션의 20% 코드에서 전체 결함의 80%가 발견
  • 살충제 패러독스
    • 동일한 테스트 케이스 반복하면 더 이상 결함이 반복되지 않음
  • 테스팅은 정황(Context)에 의존적
    • 정황에 따라 테스트 결과가 달라질 수 있기에, 정황에 따른 테스트 수행
  • 오류-부재의 궤변
    • 결함을 모두 제거해도 사용자 요구사항 만족시키지 못하면 품질이 높지 못함

어플리케이션 테스트 분류

실행 여부 따라

  • 정적 테스트 따라 -> 실행X
    • 워크스루(검토회의) -> 전문가 직접 검토, 절차 따라, 오류 조기 검출
    • 인스펙션 -> 워크스루의 발전, 산출된 결과물 품질 평가 및 개선 방법 제시
  • 동적 테스트 -> 실행 O, 모든 단계
  • 화이트박스 테스트 / 블랙박스 테스트

테스트 기반에 따라

  • 명세 기반 테스트 -> 사용자 요구사항 명세
  • 구조 기반 테스트 -> SW 내부 논리 흐름
  • 경험 기반 테스트 -> 테스터의 경험, 체크리스트

시각에 따라

  • Verification 검증 테스트 -> 개발자 시각, 제품 명세서
  • Validation 확인 테스트 -> 사용자 시각, 사용자 요구사항

목적에 따라

  • Recovery 회복 테스트 -> 실패시키고 올바르게 복구되는가
  • Security 안전 테스트 -> 보호 도구가 볼법 침입으로부터 보호하는가
  • Stress 강도 테스트 -> 과부하 시 정상적으로 실행되는가
  • Performance 성능 테스트 -> 실시간 성능, 전체 효율성 등 응답시간 및 처리량
  • Structure 구조 테스트 -> 내부 논리적 경로, 소스코드 복잡도
  • Regression 회귀 테스트 -> 변경/수정된 코드에 새로운 결함이 없는가. 즉 반복 테스트
  • Parallel 병행 테스트 -> 변경된 SW와 기존 SW 동일한 데이터 입력해서 결과 비교

테스트 기법에 따라

화이트박스 테스트

  • 내부 논리적 경로 및 모듈 안의 내용 볼수 있어서, 내부의 논리적인 모든 경로 테스트
  • 종류
    • 기초경로 검사 -> 대표적. 절차적 설계의 논리적 복잡성 측정
    • 제어구조 검사 -> 조건 검사: 논리적 조건
  • 검증 기준
    • 문장 검증 기준 -> 모든 구문 한 번 이상
    • 분기 검증 기준 -> 모든 조건문의 조건식 결과가 (True, False) 한 번 이상
    • 조건 검증 기준 -> 조건문의 개별 조건식 결과가 (True, False) 한 번 이상
    • 분기/조건 기준 -> 분기 검증 기준, 조건 검증 기준 모두 만족

블랙박스 테스트

  • 각 기능이 완전히 작동되는 것을 입증하는 테스트
  • 종류
    • 동치 분할 검사 Equivalence Partitioning Testing
      • 타당/타당하지 않은 입력 자료 개수가 균등할 때, 입력자료에 맞는 결과가 출력되는지 확인
    • 경계값 분석 Boundary Value Analysis
      • 중간값보다 경계값에서 오류 발생 확률 높음. 경계값을 테스트 케이스로
    • 원인-효과 그래프 검사 Cause-Effect Graphing Test
      • 입력 데이트간 관계와 출력에 영향 미치는 상황 분석 후, 효용성 높은 테스트 케이스 선정
    • 오류 예측 검사 Error Guessing
      • 과거의 경험. 확인자 감각으로 테스트
    • 비교 검사 Comparison Testing
      • 여러 버전에 동일한 테스트 자료 제공, 동일한 결과 출력되는지 테스트
        => 동적 테스트, 명세 기반 테스트, 경험 기반 테스트에 해당

728x90
반응형

'백엔드 Backend' 카테고리의 다른 글

[보안] 보안 공격 종류  (0) 2024.11.04
[결합도/응집도] 결합도와 응집도란  (0) 2024.10.24
악성코드 종류  (0) 2024.10.16
SOLID 원칙  (0) 2024.10.08
비동기 Asynchronous 란?  (0) 2024.10.04
728x90
반응형

인덱스

  • 검색 속도를 높이기 위한 색인 기술
  • JOIN / WHERE 에 사용됨
  • 인덱스 없는 데이터 조회 시
    • 전체 데이터 페이지의 첫 레코드 -> 마지막 레코드까지 모두 조회 == FULL SCAN
    • 때문에 속도가 느림
  • 그러나 INDEX를 많이 설정한다고 조회 속도가 빨라지지 않음
    • INDEX는 테이블 형태로 저장하는 것임
    • 즉 인덱스가 많아지면 데이터베이스 메모리를 많이 잡아 먹음
    • 인덱스로 지정된 칼럼 값이 변동되면, 인덱스 테이블이 갱신되므로 느려짐
    • 때문에 쿼리문 자체가 빨라질 수도 있지만, 전체적 데이터베이스 부하가 증가함
  • SELECT는 빠르지만, UPDATE / INSERT / DELETE 속도는 느림
    • UPDATE / DELETE 시 WHERE을 통해 데이터 조회 자체는 빠름
    • 그러나 데이터 변경 / 삭제 자체는 느림

  • 카디널리티 : 카디널리티가 높으면 인덱스 설정에 좋은 칼럼
    • 카디널리티가 높다 == 한 칼럼이 갖고 있는 값의 중복도가 낮음 (대부분 다른 값을 가짐)
    • 인덱스 통해 불필요한 데이터 대부분을 걸러낼 수 있음
  • 선택도: 선택도가 낮으면 인덱스 설정에 좋음
    • 선택도가 높다 == 한 칼럼이 갖고 있는 값 하나로 여러 row가 찾아진다.
  • 조회 활용도: 조회 활용도가 높으면 인데스 설정에 좋은 칼럼
    • WHERE 문의 대상 칼럼으로 많이 활용되는지
  • 수정 빈도: 수정 빈도가 낮으면 인덱스 설정에 좋은 칼럼
    • 인덱스도 테이블
    • 따라서 지정된 칼럼 값이 인덱스 테이블이 새롭게 갱시되어야 함

용어

  • HEAP: 데이터 저장 시 내부적으로 아무런 순서 없이 저장된 데이터 저장 영역
    CREATE INDEX user_idx ON user_table (user_name);
728x90
반응형
728x90
반응형

UBUNTU에 Go 설치하기

  • 항상 컨테이너로 GO 프로그램을 돌렸지만, 불가피하게 로컬에 GO를 설치해 빌드하고 돌려야 할 상황이 와서 해당 내용을 기록한다.

소스 다운 받아서 설치하기

최신 버전 확인

  • 공식 사이트의 Golang 버전 확인 후 다운받기 (2024.10.21 시점 1.23.2)
  • 공식 사이트

WGET을 이용해 다운로드

  • WGET을 이용해 다운받기
    wget https://go.dev/dl/go1.23.2.linux-amd64.tar.gz

압축 해제

  • 다운 받은 압축 파일 해제
    sudo tar -C /usr/local -xzf go1.23.2.linux-amd64.tar.gz

경로 설정

vi ~/.profile

# 파일 마지막 줄 아래에 내용 추가
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

변경 사항 적용

source ~/.profile

Go 버전 확인

go version

UBUNTU 피키지 매니저로 다운받기

패키지 인덱스 업데이트

sudo apt update

GO 설치

sudo apt-get install golang-go

Go 버전 확인

go version
728x90
반응형

'백엔드 Backend > Golang' 카테고리의 다른 글

[gRPC] Go 언어로 gRPC 사용해보기  (2) 2024.12.24
[Neo4J] 데이터 핸들링하기  (1) 2024.12.09
[Neo4j] Golang 으로 쿼리 핸들링  (0) 2024.12.06
[Neo4j] Golang으로 Neo4j 연동  (0) 2024.12.05
[GO] Goroutine이란?  (0) 2024.10.02
728x90
반응형

아래는 Jenkins 도입 과정과, 겪었던 시행 착오들에 대한 과거 기록들이다.

  • 배포 과정은 아래와 같다.
    • Master 브랜치에 커밋이 발생함을 감지 (Merge 커밋)
    • Matser 브랜치의 소스 코드를 가져와 Docker 이미지 빌드
    • 빌드된 이미지를 Private Registry에 푸시
    • 배포할 서버에 .env 파일과 docker-compose.yml 파일 전송
    • 배포할 서버에 빌드된 이미지를 pull 하여 컨테이너 구동
  • 이를 위해선 Github 연동과 Credentials 생성이 우선이다.

Github 설정

Github SSH KEY 연동

  • 배포용으로 사용할 키 새성
ssh-keygen -t rsa -f jenkins
  • .pub 키를 복사해서 계정/조직의 ssh 키를 추가해 복사해 넣는다
  • Jenkins Credentials 추가
    • Kind: SSH Username with private key
    • ID 임의 설정
    • username: 깃허브 계정명
    • Private key: 생성된 jenkins 키 내용 전체 복사 붙여넣기
    • 이렇게 설정하니, Host key verification failed. 라고 하며 git fetch 실패 에러가 뜨더라
  • Jenkins Secure 설정
    • 위 에러를 해결하기 위해 아래처럼 했다
    • Git Host Key Verification Configuration 설정 변경
      • Accep First connection 설정

Github Access Token

  • 깃헙 계정 에서 personal access token 생성
    • settings - developer settings - personal access tokens - tokens(classic)
    • Classic Token 생성하기
      • exiration: No expiration
      • repo
      • admin:org
      • admin:repo_hook
    • 생성되는 값 복사해두기

Github WebHook 설정

  • 레포지토리/조직의 설정으로 이동
  • 탭 중 webhook 선택
  • Add webhook 선택
    • url에 "${jenkins-url}/github-webhook/"입력
    • 생성하면, 성공 시 초록색 체크와 함께 완료된다
    • 실패 시, 이미 실패한 요청을 선택하여 redeliver하면 해결되더라

Jenkins 설정

Credential 설정

  • Jenkins 관리로 이동
  • Credentials - Stores scoped to Jenkins의 System 클릭 - Global credentials 클릭 -> Add Credentials 클릭
  • 깃허브 레포지토리 / 조직 접근 키 설정 - 깃헙 웹훅 사용 용
    • Kind를 Secret Text로 설정
    • Secret에 위에서 복사한 Personal Access Token 값 붙여넣기
    • ID: 마음껏 설정 - 나중에 이 값으로 깃헙 연동해야 하므로 알 수 있게끔 설정
    • Description: 설명
  • 개인 계정 - 아이템에서 깃헙 레포지토리 접근 용
    • Kind를 Username with password
    • username: 깃허브 계정 명
    • password: Personal Access Token 값
    • ID: 마음껏 설정
    • Description
  • 서버 - 접속 SSH 키 세팅
    • Kind를 Username and SSH
    • private key에 SSH private key 입력
  • 환경변수 파일 - 배포시 서버에 파일 전송.
    • Kind: Secret File
    • Id: 서비스 별로 분리할 수 있게끔 설정
    • 환경변수 파일 업로드

Github webhook 설정

  • Jenkins 관리로 이동
  • System 선택
    • 하단의 Github 탭의 Github Server 클릭
    • Name에 아무거나 임의로 입력
    • API URL은 건들지 말고
    • Credentials 에 위에서 secret text로 생성한 Credentials 선택
    • Manage Hooks 체크
    • Test connection 선택하여 연동 여부 체크

Jenkins Item 설정

  • 실질적인 파이프라인 설정 내용
  • 새로운 Item 클릭

Jenkinsfile로 사용할 시

  • 소스코드 내에 Jenkinsfile을 위치시켜 이를 사용해서 빌드하는 방법
  • Jenkinsfile에서 사용할 키는 위의 SSH Credential
  • pipeline 선택
    • Github project 체크
      • project url 선택: git@github.com:{브랜치명}
      • Github hook trigger for GITScm polling 선택
      • pipeline 변경
        • Definiton을 Pipeline script from SCM 선택
        • SCM을 Git 으로 변경
        • Repository URL에 깃헙 레포 주소 입력
        • Credentials: 깃허브 SSH Credential 선택
        • Branch Specifier: 연동할 브랜치
  • 이제 master 브랜치로 커밋이 감지될 때 마다 Jenkins가 레포 안의 Jenkinsfile을 감지해서 입력된 순서대로 동작할거다

Jenkins 파일 사용 안할 시

  • freestyle project 체크
  • 소스코드 관리 - Git 체크
  • Repository Url: 깃헙 레포 주소 입력
  • Credentials
  • 깃허브 SSH Credential 선택
    • master: 연동할 브랜치 설정 * 빌드 유발
    • Github hook trigger for GITScm polling 선택 * 저장`
  • 이제 Build Steps에 과정을 입력한다.

Plugins

  • Docker common: 도커 통합 플러그인
  • Docker Pipeline
  • SSH Pipeline steps
  • SSH Server
  • Publish Over SSH

에러 해결 - 1 docker.sock 퍼미션

  • Jenkins 빌드 과정에서 도커 이미지 빌드 내용이 있었다
  • 여기에서 permission denied while trying to connect to the Docker daemon socket 에러 발생
  • 결국 호스트의 var/run/docker.sock을 마운트하고 있기 때문이므로, 이를 수정해주었다.
  • # 새로운 계정 생성 sudo adduser jenkins

권한 부여

sudo usermod -a -G docker jenkins
  • 그럼에도 해결되지가 않았다
  • 누군가는 chmod 666으로 변경하면 된다지만, 이럴 경우 권한이 너무 열려버려서 위험해질 수 있다고 한다.
  • 아무튼 확인 결과 컨테이너 내부의 docker.sock은 root:999 으로 잡혀있었다.
    • root:docker 혹은 docker:1000이 되어야 할텐데
  • 때문에 컨테이너 내부 접근해서 직접 퍼미션 체크를 해 줬다.
sudo docker exec -it -u root jenkins-host /bin/bash
chown root:docker /var/run/docker.sock
  • 차후에 퍼미션 문제 없이 마운트 시키고 싶다

에러 해결 - 2 bad crum

curl -v -X GET https://{jenkins_url}/crumbIssuer/api/json --user dong:samquinnWkd1

curl -X POST https://{jenkins_url}/job/manage/credentials/store/system/domain/_/createCredentials/build --user dong:samquinnWkd1 -H 'Jenkins-Crumb: 9e107e40a5f7b6597e4020a4680bdda68b6b04c480aba07311166caa3bf98f9f'
728x90
반응형

'데브옵스 devOps > Jenkins' 카테고리의 다른 글

[JENKINS] JenkinsFile 에서의 빌드 위치 및 빌드  (0) 2024.12.23
[Jenkins] 젠킨스란?  (2) 2024.10.07
728x90
반응형

레디스 개요 복습

  • REDIS Remote Dictionary Server
    • 디스크가 아닌 주 메모리(RAM)에 데이터를 저장하는 데이터베이스
      • 따라서 데이터가 주메모리보다 크면 안됨
    • 단일 스레드로 설계됨
    • 디스크 검색이 필요한 다른 DBMS보다 자료 접근이 훨신 빠름
    • 때문에 성능 향을 위한 캐시 서버로 자주 사용됨
      • 자주 접근하는 데이터 및 계산에 많은 시간이 소요되는 데이터를 캐싱해 빠른 접근 제공
  • 키 - 값 쌍을 가진 JSON 객체를 저장하는, 스키마 없는 데이터 베이스(NoSQL)

Redis Command

KEY 관련 명령어

  • SET (key, value): 키 - 값 쌍을 설정
  • GET (key): 주어진 키에 대한 값 조회
  • DEL (key): 주어진 키 삭제
  • EXISTS (key): 키 존재 여부 확인
  • FLUSHALL: 모든 데이터 삭제
  • KEYS (pattern): 특정 패턴을 가진 키 전체 조회
  • SETEX (key, seconds, value): 특정 시간 후에 만료되는 키 - 값 쌍 설정
  • TTL (key): 키의 만료까지 남은 시간 리턴

LIST 관련 명령어

  • LPUSH (key, value): 배열 가장 첫번째에 요소(value) 추가
  • RPUSH (key, value): 배열 가장 마지막에 요소(value) 추가
  • LRANGE (key, startIndex, stopIndex): 시작 인덱스(startIndex)와 종료 인덱스(stopIndex) 사이의 요소 목록 리턴
  • LPOP (key): 배열의 가장 첫번째 요소 제거
  • RPOP (key): 배열의 가장 마지막 요소 제거

HASH 관련 명령어

  • 단일 키 내에 {키 - 값} 쌍 저장이 가능
    • HSET (key, field, value): 키(field) - 값(value) 쌍을 해시 안에 세팅
    • HGET (key, field): 해시 안의 키(field)의 값을 가져옴
    • HGETALL (key): 해시의 모든 키-값 쌍 조회
    • HDEL (key, field): 해시에서 주어진 키(field) 삭제
    • HEXISTS (key, field): 해시 내의 키(field) 존재 여부 확인
728x90
반응형

'데브옵스 devOps > Redis' 카테고리의 다른 글

[REDIS] Redis 란?  (0) 2024.10.25
728x90
반응형

REDIS Remote Dictionary Server

  • 디스크가 아닌 주 메모리(RAM)에 데이터를 저장하는 데이터베이스
    • 따라서 데이터가 주메모리보다 크면 안됨
  • 단일 스레드로 설계됨
  • 디스크 검색이 필요한 다른 DBMS보다 자료 접근이 훨신 빠름
    • 때문에 성능 향을 위한 캐시 서버로 자주 사용됨
      • 자주 접근하는 데이터 및 계산에 많은 시간이 소요되는 데이터를 캐싱해 빠른 접근 제공
  • 키 - 값 쌍을 가진 JSON 객체를 저장하는, 스키마 없는 데이터 베이스(NoSQL)
  • 장점
    • 인메모리 키 - 값 저장소: 순수한 메모리 읽기는 빠른 읽기/쓰기 속도 및 빠른 응답 제공
    • IO 다중화(멀티플렉싱): 단일 스레드가 여러 개의 열린 소켓 연결에서 동시에 대기
    • 저수준 데이터 구조: 효율적인 저수준 데이터 구조 사용
  • 단점
    • 휘발성: 시스템이 갑자기 중단되면 Redis 내의 데이터 손실 가능

Redis Cache 동작 방식

  • 클라이언트의 데이터 요청
  • Redis Cache에서 해당 키 탐색
  • 키 발견 시 - Cache Hit
    • 캐시된 데이터 응답
  • 키 발견 실패 시 - Cache Miss

728x90
반응형

'데브옵스 devOps > Redis' 카테고리의 다른 글

[REDIS] 레디스 명령어  (0) 2024.10.28
728x90
반응형

결합도 Coupling

  • 외부 모듈과의 연관도 또는 모듈 간의 상호의존성을 나타내는 정도
  • 소프트웨어 구조에서 모듈간의 관련성을 측정하는 척도
  • 한 모듈이 변경되기 위해서 다른 모듈의 변경을 요구하는 정도
    • 결합도가 높을 수록 함께 변경해야 하는 모듈의 수가 늘어나게 됨

결합도 특징

  • 모듈 연관성 없음
  • 인터페이스 의존성
  • 복잡성 감소
  • 파급효과 최소화

결합도의 유형

  • 자료 < 스탬프 < 제어 < 외부 < 공통 < 내용
  • 내용 결합도
    • 다른 모듈 내부에 있는 변수나 기능을 다른 모듈에서 사용하는 경우의 결합도
    • 하나의 모듈이 직접적으로 다른 모듈의 내용을 참조할 때 두 모듈은 내용적으로 결합된 경우의 결합도
  • 공통 결합도
    • 파라미터가 아닌 모듈 밖에 선언되어 있는 전역 변수를 참조하고, 전역 변수를 갱신하는 식으로 상호작용 하는 경우의 결합도
  • 외부 결합도
    • 모듈이 다수의 관련 기능을 가질 때 모듈 안의 구성요소들이 그 기능을 순차적으로 수행할 경우의 결합도
  • 제어 결합도
    • 어떤 모듈이 다른 모듈의 내부 논리 조직을 제어하기 위한 목적으로 제어 신호를 이용해 통신하는 경우의 결합도
    • 하위 모듈에서 상위 모듈로 제어 신호가 이동하여 상위 모듈에게 처리 명령을 부여하는 권리 전도 현상 발생하는 결합도
  • 스탬프 결합도
    • 모듈 간의 인터페이스로 배열이나 객체, 구조 등이 전달되는 경우의 결합도
  • 자료 결합도
    • 모듈 간의 인터페이스로 전달되는 파라미터를 통해서만 모듈간의 상호 작용이 일어나는 경우의 결합도

응집도 Cohesion

  • 모듈의 독립성을 나타내는 개념
  • 모듈 내부 구성요소 간 연관 정도 의미
  • 하나의 모듈은 하나의 기능을 수행하는 것을 의미
  • 즉 변경이 발생할 때 모듈 내부에서 발생하는 변경의 정도
    • 하나의 변경을 수용하기 위해 모듈 전체가 변경 -> 높은 응집도
    • 하나의 변경을 수용하기 위해 일부 모듈의 일부만 변경 -> 낮은 응집도

특징

  • 유사기능 영역 구성
  • 단일 책임 할당
  • 함수 간 상호 협력

유형

  • 우연적 < 논리적 < 시간적 < 절차적 < 통신적 < 순차적 < 기능적
  • 우연적 응집도
    • 서로 간에 어떠한 의미 있는 연관 관계도 없는 기능 요소로 구성될 경우의 응집도
    • 서로 다른 상위 모듈에 의해 호출되어 처리상의 연관성이 없는 서로 다른 기능을 수행할 경우의 응집도
  • 논리적 응집도
    • 유사한 성격을 갖거나 특정 형태로 분류되는 처리 요소들이 한 모듈에서 처리되는 경우의 응집도
  • 시간적 응집도
    • 특정 시간에 처리되어야 하는 활동들을 한 모듈에서 처리할 경우의 응집도
  • 절차적 응집도
    • 모듈이 다수의 관련 기능을 가질 때 모듈 안의 구성요소들이 그 기능을 순차적으로 수행할 경우의 응집도
  • 통신적 응집도
    • 동일한 입력과 출력을 사용하여 다른 기능을 수행하는 활동들이 모여 있을 경우의 응집도
  • 순차적 응집도
    • 모듈 내에서 한 활동으로부터 나온 출력 값을 다른 활동이 사용할 경우의 응집도
  • 기능적 응집도
    • 모듈 내부의 모든 기능이 단일한 목적을 위해 수행되는 경우의 응집도

결론

  • 응집도는 높아야 하고, 결합도는 느슨해야 한다.
  • 설계를 변경하기에 편리해 지기 때문
728x90
반응형

'백엔드 Backend' 카테고리의 다른 글

[보안] 보안 공격 종류  (0) 2024.11.04
[테스팅] 어플리케이션 테스팅  (2) 2024.11.01
악성코드 종류  (0) 2024.10.16
SOLID 원칙  (0) 2024.10.08
비동기 Asynchronous 란?  (0) 2024.10.04

+ Recent posts