CleanCode TIL (2022.01.29)

Henry ChoΒ·2022λ…„ 1μ›” 29일
0

λ…Έκ°œλΆ

λͺ©λ‘ 보기
9/31

DAY 9

πŸ”–Β μ˜€λŠ˜ 읽은 λ²”μœ„ : 4μž₯ 주석 (75~94p)


πŸ€“Β μ±…μ—μ„œ κΈ°μ–΅ν•˜κ³  싢은 λ‚΄μš©

λ‚˜μœ 주석

μ£Όμ ˆκ±°λ¦¬λŠ” 주석

  • μ΄μœ μ—†μ΄ μ˜λ¬΄κ°μ΄λ‚˜ 업무 ν‘œμ€€λ•Œλ¬Έμ— λ§ˆμ§€λͺ»ν•΄ 주석을 단닀면 μ‹œκ°„ λ‚­λΉ„ 주석을 λ‹¬κΈ°λ‘œ κ²°μ •ν–ˆλ‹€λ©΄ 졜고의 주석을 달아라
    public void loadProperties() {
        try {
            String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
            FileInputStream propertiesStream = new FileInputStream(propertiesPath);
            loadedProperties.load(propertiesStream);
        } catch (IOException e) {
            // 속성 파일이 μ—†λ‹€λ©΄ 기본값을 λͺ¨λ‘ λ©”λͺ¨λ¦¬λ‘œ 읽어 λ“€μ˜€λ‹€λŠ” μ˜λ―Έλ‹€. }
        }
    }
    이해가 μ•ˆ λ˜μ–΄ λ‹€λ₯Έ λͺ¨λ“ˆκΉŒμ§€ λ’€μ Έμ•Ό ν•˜λŠ” 주석은 λ‚­λΉ„

같은 이야기λ₯Ό μ€‘λ³΅ν•˜λŠ” 주석

  • μ½”λ“œμ˜ λ‚΄μš©μ„ λ°˜λ³΅ν•˜κ±°λ‚˜ 덜 μ–ΈκΈ‰ν•˜λŠ” 주석은 ν”Όν•˜λΌ
  • 엔진 ν›„λ“œλ₯Ό μ—΄μ–΄λ³Ό ν•„μš”κ°€ μ—†λ‹€λ©° κ³ κ°μ—κ²Œ μ•„μ–‘ λ– λŠ” 쀑고차 νŒλ§€μ›μ΄ λ˜μ§€λ§μž
    // this.closedκ°€ true일 λ•Œ λ°˜ν™˜λ˜λŠ” μœ ν‹Έλ¦¬ν‹° λ©”μ„œλ“œλ‹€.
    // νƒ€μž„μ•„μ›ƒμ— λ„λ‹¬ν•˜λ©΄ μ˜ˆμ™Έλ₯Ό λ˜μ§„λ‹€.
    public synchronized void waitForClose(final long timeoutMillis) throws Exception {
        if (!closed) {
            wait(timeoutMillis);
            if (!closed)
                throw new Exception("MockResponseSender could not be closed");
        }
    }
    μΆ”κ°€ 정보λ₯Ό 제곡 λͺ»ν•˜λŠ” μ˜λ―Έμ—†λŠ” 주석

μ˜€ν•΄ν•  여지가 μžˆλŠ” 주석

  • μœ„μ˜ νƒ€μž„μ•„μ›ƒ 주석에 λŒ€ν•΄ this.closedκ°€ λ³€ν•˜λŠ” μˆœκ°„μ— λ©”μ„œλ“œκ°€ λ°˜ν™˜λœλ‹€κ³  μ˜€ν•΄ν•  여지가 있음

의무적으둜 λ‹€λŠ” 주석

  • Javadocs 같은 주석을 의무적으둜 달 ν•„μš”λŠ” μ—†λ‹€
  • 변경이λ ₯은 git으둜 관리해야지 μ£Όμ„μœΌλ‘œ κ΄€λ¦¬ν•˜μ§€λ§λΌ

μžˆμœΌλ‚˜ λ§ˆλ‚˜ ν•œ 주석

  • λ„ˆλ¬΄λ‚˜ λ‹Ήμ—°ν•œ 사싀을 μ–ΈκΈ‰ν•˜λŠ” 주석은 βŒΒ β†’ κ°œλ°œμžκ°€ 주석을 λ¬΄μ‹œν•˜λŠ” μŠ΅κ΄€μ— λΉ μ§€κ²Œ ν•œλ‹€

ν•¨μˆ˜λ‚˜ λ³€μˆ˜λ‘œ ν‘œν˜„ν•  수 μžˆλ‹€λ©΄ 주석을 달지 마라

// μ „μ—­ λͺ©λ‘ <smodule>에 μ†ν•˜λŠ” λͺ¨λ“ˆμ΄ μš°λ¦¬κ°€ μ†ν•œ ν•˜μœ„ μ‹œμŠ€ν…œμ— μ˜μ‘΄ν•˜λŠ”κ°€?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))

미리 주석을 μ“°κ³  κ°œλ°œν–ˆμ„μˆ˜λ„μžˆκ² μ§€λ§Œ.. λ‹€κ°œλ°œν–ˆμœΌλ©΄ 정리, μ•„λž˜μ™€ 같이 λ³€κ²½

ArrayList moduleDependees = smodule.getDependSubsystems(); 
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

μœ„μΉ˜λ₯Ό ν‘œμ‹œν•˜λŠ” 주석

  • μ†ŒμŠ€νŒŒμΌμ—μ„œ νŠΉμ • μœ„μΉ˜λ₯Ό ν‘œμ‹œν•˜λ € 주석 μ‚¬μš©
    // Actions //////////////////////////////////
    μœ„μ™€ 같은 λ°°λ„ˆμ˜ κΈ°λŠ₯으둜 주의λ₯Ό ν™˜κΈ°ν•˜λŠ” 주석을 μ“Έ 수 μžˆμ§€λ§Œ κΌ­ ν•„μš”ν•  λ•Œλ§Œ μ“΄λ‹€

λ‹«λŠ” κ΄„ν˜Έμ— λ‹€λŠ” 주석

  • ν•¨μˆ˜κ°€ μž₯ν™©ν•  λ•Œ λ‹«λŠ” κ΄„ν˜Έμ— 주석 λ‹€λŠ” κ²½μš°κ°€ μžˆμ§€λ§Œ κ·ΈλŸ΄λ°”μ—λŠ” ν•¨μˆ˜λ₯Ό μ€„μ΄μž
    public class wc {
        public static void main(String[] args) {
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            String line;
            int lineCount = 0;
            int charCount = 0;
            int wordCount = 0;
            try {
                while ((line = in .readLine()) != null) {
                    lineCount++;
                    charCount += line.length();
                    String words[] = line.split("\\W");
                    wordCount += words.length;
                } //while
                System.out.println("wordCount = " + wordCount);
                System.out.println("lineCount = " + lineCount);
                System.out.println("charCount = " + charCount);
            } //try
            catch (IOException e) {
                System.err.println("Error:" + e.getMessage());
            } //catch } //main
        }
    1960λ…„λŒ€κΉŒμ§€λŠ” μ£Όμ„μœΌλ‘œ κ΄€λ¦¬ν–ˆμ§€λ§Œ μ΄μ œλŠ” μ†ŒμŠ€ 관리 μ‹œμŠ€ν…œμ΄ μžˆμœΌλ―€λ‘œ 과감히 μ§€μ›ŒλΌ

HTML 주석

  • 주석에 html νƒœκ·Έ μ‚½μž„μ€ ν”„λ‘œκ·Έλž˜λ¨Έκ°€ μ•„λ‹ˆλΌ 도ꡬ가 해야함
    /**
     * 적합성 ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•˜κΈ° μœ„ν•œ κ³Όμ—…
     * 이 과업은 적합성 ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•΄ κ²°κ³Όλ₯Ό 좜λ ₯ν•œλ‹€.
     * <p/>
     * <pre>
     * μš©λ²•:
     * &lt;taskdef name=&quot;execute-fitnesse-tests&quot;
     * classname=&quot;fitnesse.ant.ExecuteFitnesseTestsTask&quot;
     * classpathref=&quot;classpath&quot; /&gt;
     * λ˜λŠ”
     * &lt;taskdef classpathref=&quot;classpath&quot;
     * resource=&quot;tasks.properties&quot; /&gt;
     * <p/>
     * &lt;execute-fitnesse-tests
     * suitepage=&quot;FitNesse.SuiteAcceptanceTests&quot;
     * fitnesseport=&quot;8082&quot;
     * resultsdir=&quot;${results.dir}&quot;
     * resultshtmlpage=&quot;fit-results.html&quot;
     * classpathref=&quot;classpath&quot; /&gt;
     * </pre> */

μ „μ—­ 정보

  • 주석을 ꡳ이 달아야 ν•œλ‹€λ©΄ κ·Όμ²˜μ— μžˆλŠ” μ½”λ“œ κ΄€λ ¨ μ •λ³΄λ§Œ κΈ°μˆ ν•˜μž
  • μ‹œμŠ€ν…œμ˜ μ „λ°˜μ μΈ 정보λ₯Ό κΈ°μˆ ν•˜μ§€ 마라

λ„ˆλ¬΄ λ§Žμ€ 정보

  • ν₯미둜운 μ—­μ‚¬λ‚˜ κ΄€λ ¨μ—†λŠ” 정보λ₯Ό λŠ˜μ–΄λ†“μ§€ 마라
/*
RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
1λΆ€: 인터넷 λ©”μ‹œμ§€ 본체 ν˜•μ‹
6.8절. Base64 λ‚΄μš© 전솑 인코딩(Content-Transfer-Encoding)
인코딩 과정은 μž…λ ₯ λΉ„νŠΈ 쀑 24λΉ„νŠΈ 그룹을 μΈμ½”λ”©λœ 4κΈ€μžλ‘œ κ΅¬μ„±λœ
좜λ ₯ λ¬Έμžμ—΄λ‘œ ν‘œν˜„ν•œλ‹€. μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ 진행해가며, 3개λ₯Ό λ¬Άμ–΄ 8λΉ„νŠΈ μž…λ ₯
그룹을 ν˜•μ„±ν•œλ‹€. μ΄λ ‡κ²Œ λ§Œλ“€μ–΄μ§„ 24λΉ„νŠΈλŠ” 4개λ₯Ό λ¬Άμ–΄ 6λΉ„νŠΈ 그룹으둜 μ·¨κΈ‰ν•˜λ©°,
각각은 base64 μ•ŒνŒŒλ²³μ—μ„œ 단일 자릿수둜 ν•΄μ„λœλ‹€.
base64 μΈμ½”λ”©μœΌλ‘œ λΉ„νŠΈ μŠ€νŠΈλ¦Όμ„ 인코딩할 λ•Œ, λΉ„νŠΈ μŠ€νŠΈλ¦Όμ€
MSB(Most Significant Bit) μš°μ„ μœΌλ‘œ μ •λ ¬λ˜μ–΄ μžˆλ‹€κ³  κ°€μ •ν•œλ‹€. λ”°λΌμ„œ, μŠ€νŠΈλ¦Όμ—μ„œ 첫 번째 λΉ„νŠΈλŠ” 첫 8λΉ„νŠΈ λ°”μ΄νŠΈμ—μ„œ μ΅œμƒμœ„ λΉ„νŠΈκ°€ 되며, μ—¬λŸλ²ˆμ§Έ λΉ„νŠΈλŠ” 첫 8λΉ„νŠΈ λ°”μ΄νŠΈμ—μ„œ μ΅œν•˜μœ„ λΉ„νŠΈκ°€ λœλ‹€.
*/

λͺ¨ν˜Έν•œ 관계

  • 주석과 μ½”λ“œμ˜ 관계가 λͺ…확해야함
    /*
    RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
    1λΆ€: 인터넷 λ©”μ‹œμ§€ 본체 ν˜•μ‹
    6.8절. Base64 λ‚΄μš© 전솑 인코딩(Content-Transfer-Encoding)
    인코딩 과정은 μž…λ ₯ λΉ„νŠΈ 쀑 24λΉ„νŠΈ 그룹을 μΈμ½”λ”©λœ 4κΈ€μžλ‘œ κ΅¬μ„±λœ
    좜λ ₯ λ¬Έμžμ—΄λ‘œ ν‘œν˜„ν•œλ‹€. μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ 진행해가며, 3개λ₯Ό λ¬Άμ–΄ 8λΉ„νŠΈ μž…λ ₯
    그룹을 ν˜•μ„±ν•œλ‹€. μ΄λ ‡κ²Œ λ§Œλ“€μ–΄μ§„ 24λΉ„νŠΈλŠ” 4개λ₯Ό λ¬Άμ–΄ 6λΉ„νŠΈ 그룹으둜 μ·¨κΈ‰ν•˜λ©°,
    각각은 base64 μ•ŒνŒŒλ²³μ—μ„œ 단일 자릿수둜 ν•΄μ„λœλ‹€.
    base64 μΈμ½”λ”©μœΌλ‘œ λΉ„νŠΈ μŠ€νŠΈλ¦Όμ„ 인코딩할 λ•Œ, λΉ„νŠΈ μŠ€νŠΈλ¦Όμ€
    MSB(Most Significant Bit) μš°μ„ μœΌλ‘œ μ •λ ¬λ˜μ–΄ μžˆλ‹€κ³  κ°€μ •ν•œλ‹€. λ”°λΌμ„œ, μŠ€νŠΈλ¦Όμ—μ„œ 첫 번째 λΉ„νŠΈλŠ” 첫 8λΉ„νŠΈ λ°”μ΄νŠΈμ—μ„œ μ΅œμƒμœ„ λΉ„νŠΈκ°€ 되며, μ—¬λŸλ²ˆμ§Έ λΉ„νŠΈλŠ” 첫 8λΉ„νŠΈ λ°”μ΄νŠΈμ—μ„œ μ΅œν•˜μœ„ λΉ„νŠΈκ°€ λœλ‹€.
    */
    μ£Όμ„μ˜ ν•„ν„°λ°”μ΄νŠΈκ°€ 뭘 μ–˜κΈ°ν•˜λŠ”μ§€, 200을 μ™œ λ”ν•˜λŠ”μ§€ μ•Œ 수 κ°€μ—†λ‹€

ν•¨μˆ˜ 헀더

  • 짧고 ν•œ κ°€μ§€λ§Œ μˆ˜ν–‰ν•˜λ©° 이름을 잘 뢙인 ν•¨μˆ˜κ°€ μ£Όμ„μœΌλ‘œ 헀더λ₯Ό μΆ”κ°€ν•œ ν•¨μˆ˜λ³΄λ‹€ 훨씬 μ’‹λ‹€

λΉ„κ³΅κ°œ μ½”λ“œμ—μ„œ Javadocs

  • κ³΅κ°œν•˜μ§€ μ•Šμ„ μ½”λ“œλΌλ©΄ Javadocs 쓰지 말자

예제

/**
 * 이 ν΄λž˜μŠ€λŠ” μ‚¬μš©μžκ°€ μ§€μ •ν•œ μ΅œλŒ€ κ°’κΉŒμ§€ μ†Œμˆ˜λ₯Ό μƒμ„±ν•œλ‹€. μ‚¬μš©λœ μ•Œκ³ λ¦¬μ¦˜μ€
 * μ—λΌμŠ€ν† ν…Œλ„€μŠ€μ˜ 체닀.
 * <p>
 * μ—λΌμŠ€ν† ν…Œλ„€μŠ€: 기원전 276년에 리비아 ν‚€λ ˆλ„€μ—μ„œ μΆœμƒ, 기원전 194년에 사망
 * 지ꡬ λ‘˜λ ˆλ₯Ό 졜초둜 κ³„μ‚°ν•œ μ‚¬λžŒμ΄μž 달λ ₯에 μœ€λ…„μ„ λ„μž…ν•œ μ‚¬λžŒ.
 * μ•Œλ ‰μ‚°λ“œλ¦¬μ•„ λ„μ„œκ΄€μž₯을 μ—­μž„.
 * <p>
 * μ•Œκ³ λ¦¬μ¦˜μ€ μƒλ‹Ήνžˆ λ‹¨μˆœν•˜λ‹€. 2μ—μ„œ μ‹œμž‘ν•˜λŠ” μ •μˆ˜ 배열을 λŒ€μƒμœΌλ‘œ
 * 2의 배수λ₯Ό λͺ¨λ‘ μ œκ±°ν•œλ‹€. λ‹€μŒμœΌλ‘œ 남은 μ •μˆ˜λ₯Ό μ°Ύμ•„ 이 μ •μˆ˜μ˜ 배수λ₯Ό λͺ¨λ‘ μ§€μš΄λ‹€.
 * μ΅œλŒ€ κ°’μ˜ 제곱근이 될 λ•ŒκΉŒμ§€ 이λ₯Ό λ°˜λ³΅ν•œλ‹€. *
 * @author Alphonse
 * @version 13 Feb 2002 atp */
import java.util.*;
public class GeneratePrimes {
    /**
     * @param maxValueλŠ” μ†Œμˆ˜λ₯Ό μ°Ύμ•„λ‚Ό μ΅œλŒ€ κ°’
     */
    public static int[] generatePrimes(int maxValue) {
        if (maxValue >= 2) // μœ μΌν•˜κ²Œ μœ νš¨ν•œ 경우 
        {
            // μ„ μ–Έ
            int s = maxValue + 1; // λ°°μ—΄ 크기 
            boolean[] f = new boolean[s];
            int i;
            // 배열을 참으둜 μ΄ˆκΈ°ν™”
            for (i = 0; i < s; i++)
                f[i] = true;
            // μ†Œμˆ˜κ°€ μ•„λ‹Œ μ•Œλ €μ§„ 숫자λ₯Ό 제거 
            f[0] = f[1] = false;
            // 체
            int j;
            for (i = 2; i < Math.sqrt(s) + 1; i++) {
                if (f[i]) // iκ°€ 남아 μžˆλŠ” 숫자라면 이 숫자의 배수λ₯Ό κ΅¬ν•œλ‹€. {
                    for (j = 2 * i; j < s; j += i)
                        f[j] = false; // λ°°μˆ˜λŠ” μ†Œμˆ˜κ°€ μ•„λ‹ˆλ‹€.
            }
        }
        // μ†Œμˆ˜ κ°œμˆ˜λŠ”?
        int count = 0;
        for (i = 0; i < s; i++) {
            if (f[i])
                count++; // 카운트 증가
        }
        int[] primes = new int[count];
        // μ†Œμˆ˜λ₯Ό κ²°κ³Ό λ°°μ—΄λ‘œ μ΄λ™ν•œλ‹€.
        for (i = 0, j = 0; i < s; i++) {
            if (f[i]) // μ†Œμˆ˜μΌ κ²½μš°μ—
                primes[j++] = i;
        }
        return primes; // μ†Œμˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€. }
        else // maxValue < 2
            return new int[0]; // μž…λ ₯이 잘λͺ»λ˜λ©΄ λΉ„μ–΄ μžˆλŠ” 배열을 λ°˜ν™˜ν•œλ‹€.
    }
}

μ•„λž˜μ½”λ“œλ‘œ λ³€κ²½

/**
 * 이 ν΄λž˜μŠ€λŠ” μ‚¬μš©μžκ°€ μ§€μ •ν•œ μ΅œλŒ€ κ°’κΉŒμ§€ μ†Œμˆ˜λ₯Ό κ΅¬ν•œλ‹€. * μ•Œκ³ λ¦¬μ¦˜μ€ μ—λΌμŠ€ν† ν…Œλ„€μŠ€μ˜ 체닀.
 * 2μ—μ„œ μ‹œμž‘ν•˜λŠ” μ •μˆ˜ 배열을 λŒ€μƒμœΌλ‘œ μž‘μ—…ν•œλ‹€.
 * 처음으둜 남아 μžˆλŠ” μ •μˆ˜λ₯Ό μ°Ύμ•„ 배수λ₯Ό λͺ¨λ‘ μ œκ±°ν•œλ‹€. * 배열에 더 이상 λ°°μˆ˜κ°€ 없을 λ•ŒκΉŒμ§€ λ°˜λ³΅ν•œλ‹€.
 */
public class PrimeGenerator {
    private static boolean[] crossedOut;
    private static int[] result;
    public static int[] generatePrimes(int maxValue) {
        if (maxValue < 2) return new int[0];
        else {
            uncrossIntegersUpTo(maxValue);
            crossOutMultiples();
            putUncrossedIntegersIntoResult();
            return result;
        }
    }

    private static void uncrossIntegersUpTo(int maxValue) {
        crossedOut = new boolean[maxValue + 1];
        for (int i = 2; i < crossedOut.length; i++)
            crossedOut[i] = false;
    }
    private static void crossOutMultiples() {
        int limit = determineIterationLimit();
        for (int i = 2; i <= limit; i++)
            if (notCrossed(i)) crossOutMultiplesOf(i);
    }
    private static int determineIterationLimit() {
        // 배열에 μžˆλŠ” λͺ¨λ“  λ°°μˆ˜λŠ” λ°°μ—΄ 크기의 μ œκ³±κ·Όλ³΄λ‹€ μž‘μ€ μ†Œμˆ˜μ˜ μΈμˆ˜λ‹€. 
        // λ”°λΌμ„œ 이 μ œκ³±κ·Όλ³΄λ‹€ 더 큰 숫자의 λ°°μˆ˜λŠ” μ œκ±°ν•  ν•„μš”κ°€ μ—†λ‹€. 
        double iterationLimit = Math.sqrt(crossedOut.length);
        return (int) iterationLimit;
    }
    private static void crossOutMultiplesOf(int i) {
        for (int multiple = 2 * i; multiple < crossedOut.length; multiple += i; crossedOut[multiple] = true;
        }
        private static boolean notCrossed(int i) {
            return crossedOut[i] == false;
        }
        private static void putUncrossedIntegersIntoResult() {
            result = new int[numberOfUncrossedIntegers()];
            for (int j = 0, i = 2; i < crossedOut.length; i++)
                if (notCrossed(i)) result[j++] = i;
        }
        private static int numberOfUncrossedIntegers() {
            int count = 0;
            for (int i = 2; i < crossedOut.length; i++)
                if (notCrossed(i)) count++;
            return count;
        }
    }

첫 번째 주석: generatePrimse λΌλŠ” ν•¨μˆ˜ μ΄λ¦„μœΌλ‘œ μ„€λͺ…이 되긴 ν•˜μ§€λ§Œ μ•Œκ³ λ¦¬μ¦˜μ˜ μ‰¬μš΄ 이해λ₯Ό μœ„ν•΄ 남겨둠

두 번째 주석: 루프 ν•œκ³„κ°’μœΌλ‘œ μ œκ³±κ·Όμ„ μ‚¬μš©ν•œ 이유λ₯Ό μ„€λͺ… (μ½”λ“œλ‘œ μ„€λͺ… ν•œκ³„ 있음)

πŸ€”Β λ– μ˜€λ₯΄λŠ” 생각

  • λ‚˜λŠ” μ½”λ“œκ°€ μ•„λ‹ˆμ—¬λ„ 항상 κΉŒλ§ˆκ·€μ²˜λŸΌ λ‚¨κ²¨λ‘κ±°λ‚˜ μˆ˜μ§‘ν•˜λŠ” 버릇이 μžˆμ—ˆλŠ”λ°, 이제 λͺ¨λ“  주석을 과감히 μ •λ¦¬ν•˜κ³  μ§€μ›Œ 버렀야겠닀

πŸ”ŽΒ μ§ˆλ¬Έ

πŸ“Β μ†Œκ° 3쀄 μš”μ•½

  • 엔진 ν›„λ“œλ₯Ό μ—΄μ–΄λ³Ό ν•„μš”κ°€ μ—†λ‹€λ©° κ³ κ°μ—κ²Œ μ•„μ–‘ λ– λŠ” 쀑고차 νŒλ§€μ›μ΄ λ˜μ§€λ§μž
  • 주석을 ꡳ이 달아야 ν•œλ‹€λ©΄ κ·Όμ²˜μ— μžˆλŠ” μ½”λ“œ κ΄€λ ¨ μ •λ³΄λ§Œ κΈ°μˆ ν•˜μž
  • 1960λ…„λŒ€κΉŒμ§€λŠ” μ£Όμ„μœΌλ‘œ νžˆμŠ€ν† λ¦¬ κ΄€λ¦¬ν–ˆμ§€λ§Œ μ΄μ œλŠ” μ†ŒμŠ€ 관리 μ‹œμŠ€ν…œμ΄ μžˆμœΌλ―€λ‘œ 과감히 μ§€μ›ŒλΌ
profile
Full stack tech visionary

0개의 λŒ“κΈ€