반응형

해당 지점위에 마우스 커서를 손모양으로 바꾸고 싶을때가 있습니다. 이 때 사용하는 CSS 속성이 cursor 입니다.

개요

cursor 속성을 이용하면 해당 태그 위에 위치하는 마우스 커서의 모양을 바꿀 수 있습니다.

auto: 자동
default: 기본값 (화살표)
pointer: 손가락 모양 (클릭 가능한 버튼)
wait: 로딩

사용법

.btn-wait { cursor: pointer }

<style type="text/css">
    .cursors span{
        display: inline-block;
        margin: 5px;
        padding: 5px 10px;
        background-color: #d2f4ff;
        border: 2px solid #09c;
    }
</style>

<div class="cursors">
    <span style="cursor: auto">Auto</span>
    <span style="cursor: crosshair">Crosshair</span>
    <span style="cursor: default">Default</span>
    <span style="cursor: pointer">Pointer</span>
    <span style="cursor: move">Move</span>
    <span style="cursor: e-resize">e-resize</span>
    <span style="cursor: ne-resize">ne-resize</span>
    <span style="cursor: nw-resize">nw-resize</span>
    <span style="cursor: n-resize">n-resize</span>
    <span style="cursor: se-resize">se-resize</span>
    <span style="cursor: sw-resize">sw-resize</span>
    <span style="cursor: s-resize">s-resize</span>
    <span style="cursor: w-resize">w-resize</span>
    <span style="cursor: text">Text</span>
    <span style="cursor: wait">Wait</span>
    <span style="cursor: help">Help</span>
</div>

결과물

Auto Crosshair Default Pointer Move e-resize ne-resize nw-resize n-resize se-resize sw-resize s-resize w-resize Text Wait Help

참고문헌

https://ofcourse.kr/css-course/cursor-%EC%86%8D%EC%84%B1 [cursor 속성]

반응형

'프론트엔드 > CSS' 카테고리의 다른 글

[CSS] 클릭 이벤트 허용 여부 (pointer-events)  (0) 2020.07.30
반응형

점층적 생성자 패턴도 쓸 수는 있지만, 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다.

setter 를 통해서 객체값 세팅(자바빈즈 패턴) 에서는 객체 하나를 만들기 위해 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지는 일관성이 무너지게 된다.

자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없다.

이러한 객체 불변과 점층적 생성자 패턴의 대안은 빌더 패턴이다.

 

빌더는 생성할 클래스 안에 정적 멤버 클래스로 만들어두는게 보통이다.

// 코드 2-3 빌더 패턴 - 점층적 생성자 패턴과 자바빈즈 패턴의 장점만 취했다. (17~18쪽)
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // 필수 매개변수
        private final int servingSize;
        private final int servings;

        // 선택 매개변수 - 기본값으로 초기화한다.
        private int calories      = 0;
        private int fat           = 0;
        private int sodium        = 0;
        private int carbohydrate  = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
        { calories = val;      return this; }
        public Builder fat(int val)
        { fat = val;           return this; }
        public Builder sodium(int val)
        { sodium = val;        return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val;  return this; }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                .calories(100).sodium(35).carbohydrate(27).build();
    }
}

빌더의 세터 메서드들은 빌더 자신을 반환하기 때문에 연쇄적으로 호출 할 수 있다. 이런 방식을 플루언트 API 혹은 메서드 연쇄(method chaning) 라고 한다.

 

 

빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기에 좋다.

추상 클래스는 추상빌더를 구체 클래스는 구체 빌더를 갖게 한다,

// 코드 2-4 계층적으로 설계된 클래스와 잘 어울리는 빌더 패턴 (19쪽)

// 참고: 여기서 사용한 '시뮬레이트한 셀프 타입(simulated self-type)' 관용구는
// 빌더뿐 아니라 임의의 유동적인 계층구조를 허용한다.

public abstract class Pizza {
    public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
    final Set<Topping> toppings;

    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }

        abstract Pizza build();

        // 하위 클래스는 이 메서드를 재정의(overriding)하여
        // "this"를 반환하도록 해야 한다.
        protected abstract T self();
    }
    
    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone(); // 아이템 50 참조
    }
}

추상메서드인 self 를 더해 하위 클래스에서는 형변환하지 않고드 메서드 연쇄를 지원할 수 있다.

 

// 코드 2-5 뉴욕 피자 - 계층적 빌더를 활용한 하위 클래스 (20쪽)
public class NyPizza extends Pizza {
    public enum Size { SMALL, MEDIUM, LARGE }
    private final Size size;

    public static class Builder extends Pizza.Builder<Builder> {
        private final Size size;

        public Builder(Size size) {
            this.size = Objects.requireNonNull(size);
        }

        @Override public NyPizza build() {
            return new NyPizza(this);
        }

        @Override protected Builder self() { return this; }
    }

    private NyPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }

    @Override public String toString() {
        return toppings + "로 토핑한 뉴욕 피자";
    }
}
// 코드 2-6 칼초네 피자 - 계층적 빌더를 활용한 하위 클래스 (20~21쪽)
public class Calzone extends Pizza {
    private final boolean sauceInside;

    public static class Builder extends Pizza.Builder<Builder> {
        private boolean sauceInside = false; // 기본값

        public Builder sauceInside() {
            sauceInside = true;
            return this;
        }

        @Override public Calzone build() {
            return new Calzone(this);
        }

        @Override protected Builder self() { return this; }
    }

    private Calzone(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }

    @Override public String toString() {
        return String.format("%s로 토핑한 칼초네 피자 (소스는 %s에)",
                toppings, sauceInside ? "안" : "바깥");
    }
}

각각의 Calzone 과 NyPizza Builder 는 NyPizza 와 Calzone 를 반환한다. 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌 그 하위 타입을 반환하는 기능을 공변 반환 타이핑(covariant return typing) 이라 한다.

이 기능을 이용하면 클라이언트가 형변환에 신경 쓰지 않고도 빌더를 사용할 수 있다.

반응형
반응형

양방향 암호화 중 하나인 aes_encrypt 에 대해서 구현하겠습니다.

AES 함수는 데이터를 암호화와 복호화를 할 수 있는 양방향 암호화 모듈입니다.

 

mysql에 있는 기본 함수 aes_encrypt 로 암호화를 만드는 법과, 이에 따라 자바 코드에서도 암호화 및 복호화 할 수 있는 코드를 짜겠습니다.

mysql 에 있는 암호화모듈을 java 코드에서 구현해야 할 시점도 있습니다.

예를 들어 jpa 로 배치 insert 를 할 경우 db를 건건이 쿼리를 날리기도 번거롭고 말입니다.

 

그래서 mysql 에서 사용하는 aes_encrypt(f_data,f_key) 에 대한 자바 코드입니다.

 

select aes_encrypt("test", "key") 의 값은

이값은 byte 값이다. String 으로 바꾸기 위해 

select hex(aes_encrypt("test", "key")) 로 hex 도 추가합니다.

 

자바 코드는

public class CrypoUtil {


    /**
     *
     * @param key
     * @param text 암호화할 데이터
     */
    public static String encrypto(String key, String text) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
        final Cipher encryptCipher = Cipher.getInstance("AES");
        encryptCipher.init(Cipher.ENCRYPT_MODE, generateMySQLAESKey(key, "UTF-8"));
        String result = new String(Hex.encodeHex(encryptCipher.doFinal(text.getBytes("UTF-8")))).toUpperCase();

        return result;
    }
    
    /**
     * @param key
     * @param text 암호화 풀 데이터
     */
    public static String decrypto(String key, String text) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, DecoderException {
        final Cipher decryptCipher = Cipher.getInstance("AES");
        decryptCipher.init(Cipher.DECRYPT_MODE, generateMySQLAESKey(key, "UTF-8"));
        String result = new String(decryptCipher.doFinal(Hex.decodeHex(text.toCharArray())));

        return result;
    }
    
    public static void main(String[] args) throws NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException, DecoderException {
        String encrypTxt = encrypto("key", "test");
        System.out.println(encrypTxt);
        String decrypTxt = decrypto("key", encrypTxt);
        System.out.println(decrypTxt);
    }
}

여기서 주의 할 점은 encryptCipher.doFinal(text.getBytes("UTF-8")))) 이 값의 리턴값이 byte 라는 것입니다. 물론 mysql 에서도 aes_encrypt 도 byte 를 리턴해줘서 hex 로 변환해줘서 문자열로 변환해줘야 합니다,

반응형

'Java' 카테고리의 다른 글

[java] java 로 gzip 압축 및 풀기  (0) 2020.08.10
[자바] Intellij 에서 javadoc 문서 만들기  (0) 2020.06.02
junit 사용  (0) 2020.05.08
반응형

2장. 객체 생성과 파괴

아이템 1. 생성자 대신 정적 팩터리 메서드를 고려해라.

정적팩터리 메서드가 생성자보다 좋은 장점 다섯가지

1. 이름을 가질수 있다.

생성자에 넘기는 매개변수와 생성자 자체로는 반환되는 객체의 특성을 알 수 가 없다. 

BigInteger (int, int, Random) vs BigInteger.probablePrime 중에 값이 소수인 BigInteger 를 반환하는 것을 알기 쉽다.

 

2. 호출될 떄마다 인스턴스를 새로 생성하지는 않아도 된다.

Boolean.valueOf(boolean) 메서드는 객체를 생성하지 않는다. 

반복되는 요청에 같은 객체를 반환하는 식으로 언제 어느 인스턴스를 살아 있게 할지를 철저히 통제 가능. instance-controlled (인스턴스 통제)

 

3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

API를 만들 때 이 유연성을 응용하면 구현 클래스를 공개하지 않아도 그 객체를 반환할 수 있어 API를 작게 유지할 수 있다.

자바 8 부터 인터페이스를 정적 메소드로 선언 가능하다.

 

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

반환 타입의 하위 타입이기만 하면어떤 클래스의 객체를 반환하든 상관 없다.

 

5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

이런 점은 서비스 제공자 프레임워크(service provider framework)를 만드는 토대가 된다. 대표적인 예시로는 JDBC. 

서비스 제공자 프레임워크는 3개의 핵심 컴포넌트로 구성.

- 구현체의 동작을 정의하는 서비스 인터페이스

- 제공자가 구현체를 등록 할 때 사용하는 제공자 등록 API

- 클라이언트가 서비스의 인스턴스를 얻을 떄 사용하는 접근 API

여기서 서비스 접근 API 가 유연한 정적 팩터리 이다.

서비스 제공자 API 인터페이스가 없다면 각 구현체를 인스턴스로 만들 떄 리플렉션을 사용해야 한다.

JDBC 에서는 Connectin 이 서비스 인터페이스 역할을, DriverManager.registerDriver 가 제공자 등록 API 역할.

DriverManager.getConnection 이 서비스 접근 API 역할을, Driver 가 서비스 제공자 인터페이스 역할을 수행한다.

여기서 서비스 접근 API 는 공급자가 제공하는 것보다 더 풍부한 서비스 인터페이스를 클라이언트에 반환할 수 있다.

 

단점

1. 상속을 하려면 public 이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.

2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다. 네이밍으로 해결

  • from: 매개변수를 받아 해당 타입의 인스턴스를 반환하는 형변환 메서드 
    ex) Date d = Date.from(instant)
  • of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드 
    ex) Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING)
  • valueOf : from 과 of 의 더 자세한 버전 -                                                                                                                                   ex) BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE)
  • instance 혹은 getInstance : 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장 하지 않는다.                              ex) StackerWaker luke = StackWalker.gtInstance(options)
  • create 혹은 newInstance : 매번 새로운 인스턴스를 생성해 반환                                                                                                  ex) Object newArray = Array.newInstance(classObject, arrayLen)
  • getType : 생성할 클래스가아닌 다른 클래스에 정의할 때 사용.
    ex) FileStore fs = Files.getFileStore(path)
  • newType : 생성할 클래스가 아닌 다른 클래스에 정의할 때 사용 
    ex) BufferedReader br = Files.newBufferedReader(path)
  • type: getType 과 newType 의 축약 
    ex) List<Company> litnay = Collection.list(legacyLitany)

 

정리

정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하자.

그래도 정적팩터리를 사용하는게 유리한 경우가 더 많으므로, 무조건 생성자를 제공하는 습관은 버리자.

 

반응형
반응형

이펙티브 자바 3판입니다.

이 책은 자바 중급 이상의 책이고, 자바 개발자들이 한번쯤 읽어봐야 할 책으로 중급개발자로 나아가기 위한 책이라고 많이 들어서 정주행 하려고 합니다.

자바 8에 대한 이해도를 높이기 위해서도 이 책을 추천할 만한 책인 것 같습니다.

 

 

책의 예제 코드입니다.

https://github.com/WegraLee/effective-java-3e-source-code

 

WegraLee/effective-java-3e-source-code

『이펙티브 자바, 3판』(인사이트, 2018). Contribute to WegraLee/effective-java-3e-source-code development by creating an account on GitHub.

github.com

책 서평에 적혀 있는 공식 강의입니다.

백기선님의 유튜브 강의입니다. 정말 대단하신 분이네요. 인프런 강의도 엄청 많은데,,,,,

https://www.youtube.com/watch?v=X7RXP6EI-5E&list=PLfI752FpVCS8e5ACdi5dpwLdlVkn0QgJJ

 

반응형
반응형

프론트에서 개발하다보면 api 중에 시간소요가 1초 이상 소요되어 화면에 랜더링 되기 전에 어떤 구성된 화면이 나왔으면 하는 순간이 있습니다.

왜냐하면 api 로 데이터를 받아올 때 까지 로딩되는 화면에는 아무런 리스트 화면도 보여주지 않으면, 사용자 입장에서 어색한 느낌이 있습니다.

이럴때 쓰는 로딩 방식보고 skeleton 로딩이라고 합니다.

vue 에 라이브러리가 있나 찾아보았는데, 라이브러리 보다 css 만으로도 처리가 가능한 리소스를 찾아 내용 정리합니다.

1. vue skeletonBox 소스

<template>
  <span
    :style="{ height, width: computedWidth }"
    class="SkeletonBox"
  />
</template>

<script>
export default {
  name: `SkeletonBox`,
  props: {
    maxWidth: {
      default: 100,
      type: Number
    },
    minWidth: {
      default: 80,
      type: Number
    },
    height: {
      default: `1em`,
      type: String
    },
    width: {
      default: null,
      type: String
    }
  },
  computed: {
    computedWidth () {
      return this.width || `${Math.floor((0.9 * (this.maxWidth - this.minWidth)) + this.minWidth)}%`
    }
  }
}
</script>

<style lang="scss">
.SkeletonBox {
  display: inline-block;
  position: relative;
  vertical-align: middle;
  overflow: hidden;
  background-color: #DDDBDD;
  &::after {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    transform: translateX(-100%);
    background-image: linear-gradient(
      90deg,
      rgba(#fff, 0) 0,
      rgba(#fff, 0.2) 20%,
      rgba(#fff, 0.5) 60%,
      rgba(#fff, 0)
    );
    animation: shimmer 5s infinite;
    content: '';
  }
  @keyframes shimmer {
    100% {
      transform: translateX(100%);
    }
  }
}
</style>

2. 사용예시

<template>
<table summary="샘플">
  <colgroup>
    <col style="width:200px"/>
    <col style="width:340px"/>
    <col style="width:150px"/>
    <col/>
  </colgroup>
<tbody>
  <template v-if="isFirstLoading">
    <tr>
      <th><skeleton-box/></th>
      <td colspan="3"> <skeleton-box /><skeleton-box/><skeleton-box/></td>
    </tr>
    <tr>
        <th><skeleton-box/></th>
        <td colspan="3"> <skeleton-box/><skeleton-box/><skeleton-box/></td>
    </tr>
    <tr>
        <th><skeleton-box/></th>
        <td colspan="3"> <skeleton-box/><skeleton-box/><skeleton-box/></td>
    </tr>
    <tr>
        <th><skeleton-box/></th>
        <td colspan="3"> <skeleton-box/><skeleton-box/><skeleton-box/></td>
    </tr>
  </template>
  <template v-for="listItem in list">
   <!-- list 내용 -->
  </template>
</tbody>
</template>

<script>
import SkeletonBox from '@/components/common/SkeletonBox'

export default {
  name: 'ModalAttribute',
  components: {
    SkeletonBox
  },
  data () {
    return {
      isFirstLoading: true,
      list: []
    }
  },
}

위 소스코드가 예시로 짠 소스코드입니다.

간단히 설명하자면, isFirstLoading 이 true 이면, skelecton 박스가 보이게 되는 처리입니다.

 

아래 참고문헌의 코드를 그대로 구현 한 내용이고 다른 위의 예시 소스와 다른 내용은 라인 넓이를 랜덤함수로 구현된 내용은 수정했습니다.

넓이와 세로 px 값을 넣어줄 수 있어, 별다른 npm 다운 없이 위 소스코드만 복붙해서 사용가능합니다.

 

참고

https://markus.oberlehner.net/blog/skeleton-loading-animation-with-vue/  [skeleton 화면 구성]

 

반응형
반응형

성능을 떠나서 로컬 개발시 도커로 인프라를 구성하면 조금 관리하기 편한 면이 있을 것 같아 docker 로 mysql 을 올려봤다.

간단히 docker 명령어 만으로 mysql을 올려보자.

1. mysql docker 이미지 다운로드

태그에는 MySQL 버전을 명시하며. 만약 태그에 버전을 명시하지 않으면, 최신 버전인 latest를 가져온다.

docker pull mysql:lastest

2. docker 이미지 확인

docker images

3. 저장소 설정

  • 호스트의 /Users/{내계정}/{마운트시킬폴더명} 디렉토리를 컨테이너의 /var/lib/mysql 디렉토리로 마운트
  • docker에 mysql과 같은 DB를 설치하는 경우 컨테이너 삭제와 함께 데이터도 날라가므로, 저장소는 반드시 외부 저장소를 사용한다.

4. docker 실행 명령어

docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password --name jun-mysql -v /Users/사용자명/마운트시킬폴더명:/var/lib/mysql mysql:버젼

위를 보면 docker 실행명령어가 너무 길다.
그래서 docker-compose 라는 docker 기능을 이용하여 docker 를 실행시키자

5. docker-compose 로 실행

아무 폴더경로에, 실행시키고자 하는 파일경로
docker-compose.yml 파일 생성.

version: "3.3" # 파일 규격 버전
services: # 이 항목 밑에 실행하려는 컨테이너 들을 정의
  db: # 서비스 명
    image: mysql:5.7 # 사용할 이미지\
    container_name: jun-mysql # 컨테이너 이름 설정
    ports:
      - "3306:3306" # 접근 포트 설정 (컨테이너 외부:컨테이너 내부)
    environment: # -e 옵션
      MYSQL_ROOT_PASSWORD: '루트 비밀번호'  # MYSQL 패스워드 설정 옵션
      MYSQL_USER: '계정'
      MYSQL_PASSWORD: '비밀번호'
    volumes:
      - /Users/{계정명}/{마운트할 폴더명}:/var/lib/mysql  

 

위와 같이 docker-compose.yml 이라는 명으로 파일 만든뒤
해당 파일 있는 경로에서 명령어로 파일 실행

  • 백그라운드로 실행시 -d 옵션으로 실행

    docker-compose up -d

6. docker 프로세스 확인

docker ps -a

7. mysql 컨테이너 접속

docker exec -it jun-mysql bash
여기서 jun-mysql 은 도커 컨테이너 명이다.

8. 주의사항

mysql 이 8 버젼 부터는 password 인증방식이 mysql_native_password 에서 caching_sha2_password 로 변경 되어 config 파일 변경 및 docker 실행 후 유저에 대한 인증 방식을 각각 native 로 바꿔줘야 한다.

방법1

config 파일 생성.
$ sudo nano /usr/local/opt/mysql/config/my.cnf
`

파일추가 내용
[mysqld] default-authentication-plugin=mysql_native_password

방법 2

mysql bash shell 접속 후
docker exec -it CONTAINER_ID bash

루트계정으로 로그인.
mysql --user=root --password

각 계정의 패스워드 디폴트 인증방식 변경.
ALTER USER 'username' IDENTIFIED WITH mysql_native_password BY 'password';

두가지 방법으로도 나는 해보지는 않았다.
그냥 매번 새로운 환경마다 docker-compose 로 올리면 수정을 해줘야 해서, 5.7 로 버젼을 명시하고 사용했다.

반응형
반응형

터미널에서 code . 을 입력하면 vscode 가 실행된다.

안되는 경우는 command + shift + p 에서 shell 입력 후 아래와 같이 terminal 에서 실행할 수 있게 해준다.

 

 

반응형

+ Recent posts