Code Monkey home page Code Monkey logo

cleancode's Introduction

CleanCode

  • 클린코드를 읽고 정리한 내용.

Intro

깨끗한 코드를 작성 하는 방법은 배우기 어렵다. 단순한 원칙과 패턴을 안다고 깨끗한 코드가 나오지 않는다.
스스로 연습하고 실패도 맛보며 남들이 시도하다 실패하는 모습도 봐야한다.
결정을 내리느라 고민하는 모습, 잘못된 결정으로 대가를 치르는 모습도 봐야한다.
  • 휴리스틱(heuristics) 또는 발견법(發見法)이란 불충분한 시간이나 정보로 인하여 합리적인 판단을 할 수 없거나, 체계적이면서 합리적인 판단이 굳이 필요하지 않은 상황에서 사람들이 빠르게 사용할 수 있게 보다 용이하게 구성된 간편추론의 방법(나무위키)

chapter 1. 깨끗한 코드

  • 좋은 코드와 나쁜 코드를 구분하는 능력

  • 코드를 작성하는 방법

  • 나쁜 코드를 좋은 코드로 바꾸는 실력

  • 코드는 요구사항을 상세히 표현하는 수단

    • 앞으로 프로그래밍 언어에서 추상화 수준은 점점 높아 질 것을 예상.
    • 특정 응용 분야에 적합한 프로그래밍언어의 수도 점차 많아 질 것을 예상.
  • 나쁜 코드

    • 프로그래머라면 누구나 당연히 나쁜 코드로 고생한 경험이 있다.
      • 어째서 나쁜 코드를 작성했을까?
        • 일정이 급해서?
        • 서두르느라?
        • 상사한테 욕먹을까봐?
        • 지겨워서 빨리 끝내려고?
        • 다른 업무가 너무 밀려서?
    • 나중은 결코 오지 않는다.
      • 자신이 짠 쓰레기 코드를 쳐다보며 나중에 손보겠다고 생각한 경험..?
      • 대충 짠 프로그램이 돌아간다는 사실에 안도감
  • 나쁜 코드로 치르는 대가

    • 어느정도 프로그램을 짯다면 남들이 저질러놓은 쓰레기 코드로 고생한 경험이 있다.
    • 나쁜 코드는 개발속도를 크게 떨어트린다.
    • 프로젝트 초반에는 번개처럼 나가다 1-2년 만에 굼뱅이 처럼 기어갈 수 있다.
    • 나쁜 코드가 쌓일수록 팀 생산성은 크게 떨어진다.
  • 태도

    • 코드는 왜 그렇게 되었을까?
      • 좋은 코드가 어째서 순식간에 나쁜 코드로 전락할까?
        • 원래 설계를 뒤집는 방향으로 요구사항?
        • 일정이 촉박해 제대로 할 시간이 없다?
        • 잘못은 전적으로 우리 프로그래머에게 있다.
      • 어째서 프로그래머의 잘못일까?
        • 요구사항은?
        • 일정은?
        • 바보같은 관리자와 멍청한 고객은?
        • 좋은 코드를 사수하는 일은 바로 우리 프로그래머들의 책임이다.
        • 나쁜 코드의 위험을 이해하지 못하는 관리자 말을 그대로 따르는 행동은 전문가답지 못하다.
관리자와 마케팅은 약속과 공약을 내걸며 우리에게 정보를 구한다. 우리에게 정보를 구하지 않더라도 우리가 적극적으로 정보를 제공해야 마땅하다. 
사용자는 요구사항을 내놓으며 우리에게 현실성을 자문하고 프로젝트 관리자는 일정을 잡으며 우리에게 도움을 청한다.
우리는 프로젝트를 계획하는 과정에 깊숙히 관여한다. 그러므로 프로젝트 실패는 우리에게도 커다란 책임이 있다.
특히 나쁜 코드가 초래하는 실패에는 더더욱 책임이 크다.
  • 원초적 난제

    • 우리는 근본적인 난제에 봉착한다.
    • 프로젝트 기한을 맞추려면 나쁜 코드를 양산 할 수 밖에 없다고 느낀다.
    • 그들은 빨리가려고 시간을 들이지 않는다.
    • 진짜 전문가는...?
      • 나쁜 코드를 양산하면 기한을 맞추지 못하고
      • 오히려 엉망진창인 상태로 인해 속도가 곧바로 늦어지고
      • 결국 기한을 맞추지 못한다는 것을 알고 있다.
    • 그래서 기한을 맞추는 유일한 방법은?
      • 그러니까 빨리가는 유일한 방법은 언제나 코드를 깨끗하게 유지하는 습관
  • 깨끗한 코드라는 예술

    • 깨끗한 코드를 작성하려면 '청결'이라는 힘겹게 습득한 감각을 활용하여 자잘한 기법들을 적용하는 절제와 규율이 필요
  • 깨끗한 코드란?

    • 비야네 스트롭스트룹(C++의 창시자)
      • "나는 우아하고 효율적인 코드를 좋아한다. 논리가 간단해야 버그가 숨어들지 못한다. 의존성을 최대한 줄여야 유지보수가 쉬워진다 ...(생략)"
      • 깨끗한 코드는 보기에 즐거운 코드
      • CPU 자원을 낭비하는 코드는 우아하지 못하다.
      • 프로그래머들이 대충 넘어가는 부분 중 하나가 오류 처리다. 메모리 누수, 경쟁상태, 일관성 없는 명명법이 또 다른 예이다.
    • 그래디 부치
      • "깨끗한 코드는 단순하고 직접적이다. 깨끗한 코드는잘 쓴 문장처럼 읽힌다. 깨끗한 코드는 결코 설계자의 의도를 숨기지 않는다. 오히려 명쾌한 추상화와 단순한 제어문으로 가득하다."
      • 깨끗한 코드가 잘 쓰여진 문장처럼 읽혀야 한다는 시각
      • 여태까지 자신이 읽은 책 중 진짜로 좋아했던 책을 떠올려 보자.
    • 읽으면서 짐작한 대로 돌아가는 코드가 깨끗한 코드이다.
  • 보이스카우트 규칙

    • "캠프장은 처음 왔을 때 보다 더 깨끗하게 해놓고 떠나라"
    • 잘 짠 코드가 전부는 아니다.
    • 시간이 지나도 언제나 꺠끗하게 유지해야 한다.
    • 시간이 지나면서 엉망으로 전락하는 코드도 한 둘이 아니다.
    • 그로므로 우리는 적극적으로 코드의 퇴보를 막아야 한다.
  • 보이스카우트 규칙을 적용한다면?

    • 체크아웃 할 때보다 좀 더 깨끗한 코드를 체크인 한다면 코드는 절때 나빠지지 않는다.
    • 한꺼번에 많은 시간과 노력을 투자해 코드를 정리 할 필요가 없다.
    • 변수 이름 하나를 개선
    • 조금 긴 함수 하나를 분할
    • 약간의 중복을 제거
    • 복잡한 if 문 하나를 정리
    • 시간이 지날 수록 코드가 좋아지는 프로젝트에서 작업한다고 상상해보자!
  • chpater 1. 깨끗한 코드 결론

    • 예술에 대한 책을 읽는다고 예술가가 된다는 보장은 없다.
    • 연습 또 연습!

chpater 2. 의미 있는 이름

소프트웨어에서 이름은 어디에서나 쓰인다. 
  • 의도를 분명히 밝혀라
    • 좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다.
    • 그리고 더 나은 이름이 떠오르면 개선해야 한다.
    • 변수나 함수 그리고 클래스 이름음 다음과 같은 굴직한 질문에 모두 답해야한다.
      • 변수(혹은 함수나 클래스)의 존재 이유는?
      • 수행 하는 기능은?
      • 사용 하는 방법은?
      • 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.
(X)
int d; // 경과 시간(단위 : 날짜)

(O)
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
  • 그릇된 정보를 피하자

    • 프로그래머는 코드에 그릇된 단서를 남겨서는 안 된다.
    • 예를 들어 hp, aix, sco는 변수 이름으로 적합하지 않다.
      • 유닉스 플랫폼이나 유닉스 변종을 가르키는 이름이기 때문
    • 여러 계정을 그룹으로 묶는다면?
      • (x) accountList
        • List는 프로그래머에게 특수한 의미이다. 계정을 담는 컨테이너가 실제 List가 아니라면 그릇된 정보를 제공 할 수 있다.
      • (O) accountGroup, bunchOfAccount
    • 서로 흡사한 이름을 사용하지 않도록 주의한다.
    • 유사한 개념은 유사한 표기법을 사용하자.
  • 의미 있게 구분하자

    • 컴파일러 인터프린터만 통과하려는 생각으로 코드를 구현하면 스스로 문제를 일으킨다.
    • 불용어를 추가한 이름은 아무런 정보도 제공하지 못한다.
  • 발음하기 쉬운 이름을 사용하자

    • 사람들은 단어에 능숙하고, 우리 두뇌에서 상당 부분은 단어라는 개념만 전적으로 처리한다.
    • 프로그래밍은 하나의 사회 활동이다.
  • 검색하기 쉬운 이름을 사용하자

    • 문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
    • 마찬가지로 e 라는 문자도 변수이름으로 적합하지 못하다.
    • 긴 이름이 짧은 이름보다 좋다.
    • 이름 길이는 범위 크기에 비례해야 한다.
    • 변수나 상수를 코드 여러 곳에서 사용한다면 검색하기 쉬운 이름이 바람직하다.
    • 아래 코드에서 sum은 별로 유용하진 않으나 최소한 검색이 가능하다.
    • 그냥 숫자 5를 사용한다면 5가 들어가는 이름을 모두 찾아야 한다.
(X)
    for(int j=0; j<34; j++>){
        s += (t[j] * 4)/5;
    }

(O)
    int realDaysPerIndealDay = 4;
    const int WORK_DAYS_PER_WEEK = 5;
    int sum = 0;
    for (int j=0; j< NUMBER_OF_TASKS; j++){
        int realTaskDays = taskEstimate[j] * realDayPerIndealDay;
        int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
        sum += realTaskWeeks;
    }
  • 자신의 기억력을 자랑하지 마라

    • 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다.
    • 문자 하나만 사용하는 변수 이름은 문제가 있다.
    • 때때로 사람은 자신의 정신적 능력을 과시하고 싶어 한다.
    • 전문가 프로그래머는 명료함이 최고 라는 사실을 이해 한다.
  • Class 이름

    • 클래스 이름과 객체 이름은 명사나 명사구가 적합하다.
      • (O) Customer, WikiPage, Acccount, AddressParser
      • (X) Manager, Processor, Data, Info
      • 동사는 사용하지 않는다.
  • Method 이름

    • 메서드 이름은 동사나 동사구가 적합하다.
    • (O) postPayment, deletePage, save
    • 접근자(Accessor), 변경자(Mutator), 조건자(Predicate)는 javabeen 표준에 따라 값 앞에 get, set is를 붙인다.
    • 생성자(Constructor)를 중복정의(overload) 할 때는 정적 팩토리 메서드를 사용한다.
  • 기발한 이름을 피하라

    • 이름이 너무 기발하면 유머 감각이 비슷한 사람만, 그리고 농담을 기억하는 동안만 이름을 기억한다
    • 재미난 이름보다 명료한 이름을 선택
    • 의도를 분명하고 솔직하게 표현하자
  • 한 개념에 한 단어를 사용하라

    • 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.
    • 예로 똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다.
    • 일관성 있는 어휘는 코드를 사용할 프로그래머가 반갑게 여길 선물이다.
  • 말장난을 하지 마라

    • 한 단어를 두가지 목적으로 사용하지 않는다.
    • 다른 개념에 같은 단어를 사용한다면 그것은 말장난에 불과
    • "한 개념에 한 단어를 사용"이라는 규칙을 따랏더니 예를 들어 여러 클래스에 add라는 메서드가 생겻다.
      • 모든 add 메서드는 매개변수와 변환값이 의미적으로 똑같다면 문제가 없다.
      • 하지만 때로는 프로그래머가 같은 맥락이 아닌데도 일관성을 고려해 add라는 단어를 선택한다.
      • 지금까지 구현한 add 메서드는 모두가 기존 값 두개를 더하거나 이어서 새로운 값을 만든다고 가정.
      • 새로 작성하는 메서드는 집합에 값 하나를 추가한다. 이때 메소드는 add 라고 불러도 괜찮은가?
      • 새 메소드는 기존의 add 메소드와 맥락이 다르다. insert, append 라는 이름이 적당하다.
      • 대충 훑어봐도 이해 할 코드 작성이 목표
  • 해법 영역에서 가져온 이름을 사용

    • 코드를 읽을 사람도 프로그래머라는 사실을 명심
    • 전산 용어, 알고리즘 이름 패턴 이름, 수학 용어 등을 사용해도 괜찮다.
    • 모든 이름을 문제 영역(domain)에서 가져오는 정책은 현명하지 못한다.
    • 기술 개념에는 기술 이름이 가장 적합한 선택이다.
  • 문제 영역에서 가져온 이름을 사용

    • 적절한 프로그래머 용어가 없다면 문제 영역에서 이름을 가져온다.
    • 우수한 프로그래머와 설계자라면 해법 영역과 문제 영역을 구분할 줄 알아야 한다.
  • 의미 있는 맥락을 추가

    • 스스로 의미가 분명한 이름이 없지 않다. 하지만 대다수 이름은 그렇지 못하다.
    • 클래스, 함수, 이름 공간에 넣어 맥락을 부여 한다.
    • 모든 방법이 실패하면 마지막 수단으로 접두어를 붙힌다.
    • ex)
      • firstName, lastName street, houseNumber, city, state, zipcode
      • 변수를 훑어보면 주소라는 사실을 금방 알아 챌 수 있다.
      • 하지만 어느 메서드가 state라는 변수 하나만 사용한다면? 변수 state가 주소 일부라는 사실을 금방 알 수 있을까?
      • addr이라는 접두어를 추가
      • addrFisrtName, addrLastName, addrState 라 쓰면 맥락이 좀 더 분명해진다.
      • 변수가 좀 더 큰 구조에 속한다는 사실이 분명해 진다.
  • 불필요한 맥락을 없애라

    • 고급 휘발유 충전소(Gas Station Deluxe) 라는 애플리케이션을 만든다고 가정하자
    • 모든 이름을 GSD로 시작하겠다는 생각은 전혀 바람직하지 못하다.
    • 메일 주소를 위한 클래스 GSDAccountAddress ? 고객 관리에서 고객 주소가 필요하다면? GSDAccountAddress 클래스를 사용 할 수 있는가?
    • 일반적으로 짧은 이름이 긴 이름보다 좋다. (단 의미가 분명한 경우)
  • chapter 2. 의미있는 이름 결론

    • 좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적인 배경이 같아야 한다.
    • 이것이 제일 어려운 문제고 좋은 이름을 선택하는 능력은 기술, 비즈니스, 관리 문제가 아니라 교육 문제이다.
    • 우리들 대다수가 자신이 짠 클래스 이름과 메서드 이름을 모두 암기하지 못한다.
    • 암기는 요즘 나오는 도구에게 맡기고, 우리는 문장이나 문단처럼 읽히는 코드 아니면 적어도 표나 자료 구조처럼 읽히는 코드를 짜는데 집중

chpater 3. 함수

프로그래밍 초창기에는 시스템을 루틴과 하위 루틴으로 나누었다. 포트란과 PL/1 시절에는 시스템을 프로그램, 하위 프로그램, 함수로 나눴다.
지금은 함수만 살아남았다. 어떤 프로그램이든 가장 기본적인 단위가 함수다. 
  • 작게 만들어라

    • 함수를 잘 만드는 첫번째 규칙은 작게 만드는 것이다.
  • 블록과 들여쓰기

    • 다시말해 if 문 / else문 / while 문 등에 들어가는 블록은 한줄이어야 한다는 의미
    • 바깥을 감싸는 함수가 작아질 뿐만 아니라 블록 안에서 호출하는 이름을 적절히 짓는다면 코드를 이해하기 쉬워진다.
    • 즉 중첩 구조가 생길 만큼 함수가 커져서는 안된다.
    • 함수에서는 들여쓰기 수준은 1단이나 2단을 넘어서면 안된다.
  • 한가지만 해라

    • 함수는 한가지를 해야한다. 그 한가지를 잘 해야한다. 그 한가지만 해야 한다.
    • 이때, 한가지 라는게 정확히 무엇인지 알기가 어렵다,
    • ex)
        1. 페이지가 테스트 페이지인지 판단한다.
        1. 그렇다면 설정 페이지와 해제 페이지를 넣는다.
        1. 페이지를 HTML로 렌더링 한다.
    • 위 예시는 한가지만 하는가 아니면 세가지를 하는가?
      • 위에서 언급한 세 단계는 지정된 함수 이름 아래에서 추상화 수준이 하나이다.
      • 함수는 간단한 TO 문단으로 기술 할 수 있다.
        • TO? LOGO 언어에서 사용하는 키워드. 루비나 파이썬에서 사용하는 def와 똑같다.
      • TO RenderPageWithSetupsAndTeardowns, 페이지가 테스트 페이지인지 확인 한 후 테스트 페이지라면 설정 페이지와 해제 페이지를 넣는다. 테스트 페이지든 아니든 페이지를 html로 렌더링 한다.
      • 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
  • 함수당 추상화 수준은 하나로!

    • 함수가 확실히 한가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일 해야 한다.
  • 내려가기 규칙(위에서 아래로 코드 읽기)

    • 코드는 위에서 아래로 이야기 처럼 읽혀야 좋다
    • 한 함수 다음에는 추상화 수준이 한단계 더 낮은 함수가 온다.
    • 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
  • 서술적인 이름을 사용하자

    • 코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 된다.
    • 한 가지만 하는 작은 함수에 좋은 이름을 붙힌다면 이런 원칙을 달성함에 있어 이미 절반은 성공
    • 이름이 길어도 괜찮다. 겁먹을 필요 없다.
    • 이름을 붙일 때는 일관성이 있어야 하며 모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다
      • includeSetupAndTeardownPages, includeSetupPages, includeSuiteSetupPage, includeSetupPage
  • 함수의 인수

    • 함수에서 이상적인 인수 개수는 0개(무항), 다음은 1개(단항), 다음은 2개(이항)
    • 3개 이상은 가능한 피하는 편이 좋다.
    • 출력 인수는 입력 인수보다 이해하기 어렵다.
  • 플래그 인수

    • 플래그 인수는 추하다.
    • 함수로 부울 값을 넘기는 관례는 정말로 끔찍하다.
    • 이유는 함수로 한꺼번에 여러가지를 처리한다고 공표한 셈이기 때문이다.
  • 이항 함수

    • 인수가 2개인 함수는 인수가 1개인 함수보다 이해하기 어렵다.
  • 삼항 함수

    • 삼항 함수를 만들 때는 신중히 고려해야 한다.
  • 동사와 키워드

    • 함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름은 필수다
    • 단항 함수는 함수와 인수가 동사/명사 쌍을 이루어야 한다.
    • write(String name), wirteField(String name)
  • 부수 효과를 일으키지 말자.

    • 부수 효과는 거짓말이다.
    • 함수에서 한가지를 하겠다고 약속하고 남몰래 다른일을 하는 것이다.
    • 많은 경우 시간적인 결합이나 순서 종석성을 초래한다.
    • 아래 예제에서 함수가 일으키는 부수효과는 Session.initialize() 호출이다.
    • 이름만 봐서는 세션을 초기화 한다는 사실이 드러나지 않는다.
    • 그래서 함수 이름만 보고 함수를 호출하는 사용자는 사용자를 인증하면서 기존 세션 정보를 지워버릴 위험에 있다.
    • 이러한 부수 효과가 시간적인 결합을 초래한다.
    • checkPasswordAndInitializeSession 이라는 이름이 훨씬 좋다.
    • 물론 함수가 한가지만 한다는 규을 위반하지만..!
public class UserValidator {
    private Cryptographer cryptographer;
    public boolean checkPassword(String userName, String password) { 
        User user = UserGateway.findByName(userName);
        if (user != User.NULL) {
            String codedPhrase = user.getPhraseEncodedByPassword(); 
            String phrase = cryptographer.decrypt(codedPhrase, password); 
            if ("Valid Password".equals(phrase)) {
                Session.initialize();
                return true; 
            }
        }
        return false; 
    }
}
  • 명령과 조회를 분리하라
    • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘중 하나만 해야 한다.
public boolean set(String attribute, String value);

위 함수는 이름이 attribute 인 속성을 찾아 값을 value 로 설정한 후 성공하며 treu, 실패하면 false 를 반환. 실제로 호출해서 사용한다고 하면 다음과 같이 괴상한 코드가 나온다.

if(set("username","unclebob"))

위 코드는 무슨뜻일까?

  • "username"이 "unclebob"으로 설정 되어 있는지 확인하는 코드?
  • "username"을 "unclebob"으로 설정하는 코드?
  • 함수를 호출하는 코드만 봐서는 의미가 모호하다.
  • "set"이라는 단어가 동사인지 형용사인지 분간하기 어려운 탓
if(attributeExists("username")){
  setAttribute("userName", "unclebob");
}
  • 해결책은 명령과 조회를 분리해 혼란을 애초에 뿌리뽑는 방법

  • 오류코드보다 예외를 사용하라

    • 명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반 할 수 있다.
    • 자칫하면 if 문에서 명령을 표현시긍로 사용하기 쉬운 탓이다.
    • 아래 코드는 동사/형용사 혼란을 일으키지 않는 대신 여러 단계로 중첩되는 코드를 야기
    • 오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 한다는 문제에 부딛힘.
  if(deletePage(page)==E_OK)
if (deletePage(page) == E_OK) {
  if (registry.deleteReference(page.name) == E_OK) {
    if (configKeys.deleteKey(page.name.makeKey()) == E_OK){ 
      logger.log("page deleted");
    } else {
      logger.log("configKey not deleted");
    }
  } else {
    logger.log("deleteReference from registry failed"); 
  }
} else {
  logger.log("delete failed"); return E_ERROR;
}

반면 오류 코드 대신 예외를 사용하면 오류 처리 코드 원래 코드에서 분리되 므로 코드 깔끔해진다.

try {
  deletePage(page); 
  registry.deleteReference(page.name); 
  configKeys.deleteKey(page.name.makeKey());
}
catch (Exception e) {
  logger.log(e.getMessage()); 
}
  • Try/Catch 블록 뽑아내기

  • try/catch 블록은 원래 추하다.

  • 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.

  • try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.

  public void delete(Page page) { 
    try {
      deletePageAndAllReferences(page); 
    }
    catch (Exception e) { 
      logError(e);
    } 
  }

  private void deletePageAndAllReferences(Page page) throws Exception { 
    deletePage(page);
    registry.deleteReference(page.name); 
    configKeys.deleteKey(page.name.makeKey());
  } 

  private void logError(Exception e) { 
    logger.log(e.getMessage());
  }
  • delete 함수는 모든 오류를 처리 한다.
  • 실제로 페이지를 제거하는 함수는 deletePageAndAllReferences
  • deletePageAndAllReferences 함수는 예외를 처리하지 않음
  • 정상 동작과 오류 처리 동작을 분리

cleancode's People

Contributors

kor-jimmy avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.