Practice for coding test

프로그래머스 - Level 1. 개인정보 수집 유효기간 / JavaScript (js)

Gray Park 2023. 2. 10. 11:30
728x90
반응형

문제설명

문제분석

파라미터는 순서대로 오늘 날짜, 각 약관별 유효기간, 약관별 사인한 날짜가 전달됩니다. 이 작업은 한 명에 대한 작업이고, 한 명이 사인한 약관에 대해 오늘날짜로 확인하는 작업이 진행됩니다. 문제를 풀기에 앞서, 만약 내가 실무에서 이런 작업을 진행한다고 생각해봤습니다. 아마 나는 매일 한번씩 모든 사용자에 대해 이 함수를 실행하는 대신, 매월이나 매년 또는 사인하는 순간에 큐잉할 거 같습니다. 당연히 개인정보보호법이 강화되어 시행되는 조치인만큼, 처음부터 약관을 파기하는 로직이 필요하진 않았을 것입니다. 따라서 레거시한 데이터를 바탕으로 위 작업이 진행될 것이고, 최초의 1회에 한해서는 모든 사용자를 대상으로 반드시 이 함수를 실행해야한다고 추론할 수 있습니다. 그럼에도 불구하고 시행령에는 계도기간 또는 유예기간이 존재하므로, 만약 내가 담당자라면 일자별로 실행할 인원을 분리해서 작업하거나 별개의 작업공간에서 백업된 데이터를 대상으로 진행할 것 같습니다. 그렇지 않으면 기 서비스에 어떤 형태로든 안좋은 영향이 갈 수밖에 없기 때문입니다.

 

서두가 길었습니다. 이 문제를 로직으로 정리하면 다음과 같습니다.

  1. 이 사용자가 서명한 약관과 그 날짜를 탐색합니다.
  2. 이 사용자가 약관에 서명한 날로부터 오늘 날짜까지의 기간이, 약관의 유효기간보다 길다면 지체없이 개인정보를 파기해야 합니다.
  3. 이때 파기해야할 약관의 순서를 answer에 담아 리턴합니다.

문제를 단순화 했으니 이해하기 쉽게 코드를 짜보겠습니다.

이해하기 쉽게 코드작성하기

function solution(today, terms, privacies) {
    var answer = [];
    // 약관별로 유효기간을 분류
    const termsObj = getTermsObject(terms);
    
    // 약관에 서명한 날짜와 약관이 담긴 privacies 탐색
    for(let i = 0; i < privacies.length; i++) {
        const [signDay, term] = privacies[i].split(" ");
        // 파기해야할 개인정보인지 검사
        if(isDestroyed(today, signDay, termsObj[term])) {
            answer.push(i+1);
        }
    }
    return answer;
}

// 파기해야할 개인정보인지 검사
function isDestroyed(today, signDay, termMonth) {
    const [tY, tM, tD] = getSplitedDate(today);
    const [sY, sM, sD] = getSplitedDate(signDay);
    const [termsY, termsM] = getSplitedDate(termMonth);
    const todayToDay = dateToDay(tY, tM, tD);
    const signDayToDay = dateToDay(sY, sM, sD);
    const termsToDay = dateToDay(termsY, termsM);
    
    // 약관에 서명한 날로부터 약관의 유효기간만큼의 시간이 지났는지 확인
    if(signDayToDay + termsToDay <= todayToDay) {
        return true;
    }
    return false;
}

// 연.월.일을 날짜로 변환
function dateToDay(y, m, d = 0) {
    m += y * 12;
    d += m * 28;
    return d;
}

// 연.월.일을 숫자로 변환
function getSplitedDate(yyyymmddOrNumber) {
    if(typeof(yyyymmddOrNumber) === "number") {
        const y = Math.floor(yyyymmddOrNumber / 12);
        const m = yyyymmddOrNumber % 12;
        return [y, m];
    }
    return yyyymmddOrNumber.split(".").map(x=>x[0] === "0" ? Number(x.slice(1)) : Number(x));
}

// 약관별 유효기간을 변환
function getTermsObject(terms) {
    const obj = {};
    
    terms.forEach(termAndMonth => {
        const [term, month] = termAndMonth.split(" ");
        if(obj[term] === undefined) {
            obj[term] = Number(month);
        }
    });
    
    return obj;
}

코드 설명

solution 함수만 보면 충분히 추상화되어있어 이해하기 좋습니다. 먼저 약관별로 유효기간을 정리합니다. privacies를 탐색하며 privacies의 요소에 포함된 약관의 유효기간과 서명한 날짜, 그리고 오늘 날짜를 비교하여 파기해야할 개인정보인지 분류합니다. 만약 파기해야할 개인정보라면 answer에 push하고 answer를 리턴합니다.

 

코드를 짜고 보니, 어차피 day로 비교를 하고 있습니다. 월별로 분류했던 내용을 전부 day로 재분류하고, 불필요한 함수를 합치거나 분리하고, 불명확한 함수명을 조금 더 명확하게 변환합니다.

function solution(today, terms, privacies) {
    var answer = [];
    // termsObj의 값을 Day로 변환
    const termsObj = getTermsObjectAsDays(terms);
    
    for(let i = 0; i < privacies.length; i++) {
        const [signDay, term] = privacies[i].split(" ");
        // 파기해야할 개인정보인지 보다 명확하게 하는 함수명으로 변환
        // 파라미터를 day 단위로 전달
        if(haveToDestroy(
            getDateAsDays(today),
            getDateAsDays(signDay),
            termsObj[term])
          ) {
            answer.push(i + 1);
        }
    }
    return answer;
}

// day 단위의 파라미터 비교만 하는 함수로 변환
function haveToDestroy(todayAsDay, signDayAsDay, termMonthAsDay) {
	return signDayAsDay + termMonthAsDay <= todayAsDay;
}

// "yyyy.mm.dd"를 day로 변환
// dateToDay 함수와 getSplitedDate 함수를 합침
function getDateAsDays(yyyymmdd) {
    let [y, m, d] = yyyymmdd.split(".").map(x => x[0] === "0" ? Number(x.slice(1)) : Number(x));
    
    m += y * 12;
    d += m * 28;
    
    return d;
}

function getTermsObjectAsDays(terms) {
    return terms.reduce((a,c) => {
        const [term, month] = c.split(" ");
        if(a[term] === undefined) {
            a[term] = Number(month) * 28;
        }
        return a;
    }, {});
}

solution 함수의 가독성뿐만 아니라, 각 함수의 이름과 기능이 보다 명확해졌습니다. 여전히 변수나 함수의 이름을 짓는게 참 어렵습니다. 아마 코드를 짜는 시간 중에 가장 오래 고민하는 부분이 작명하는 부분인 거 같습니다. 생각난 김에 오늘은 clean code를 조금 읽어보기로 합니다.

728x90
반응형