네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'programming language'에 해당되는 글 67건


반응형

https://www.amcharts.com

 

JavaScript charting library - amCharts 4

The most innovative charting library on the market. Easily add stunning data visualizations to JavaScript, TypeScript, Angular, React, and other apps.

www.amcharts.com

 

Demo에 가보면 예제를 볼 수 있다.

그 중에서 나는 column-with-rotated-series를 사용하였다.

 

 

ajax로 데이터를 가져와서 Chart를 뿌려주는 것을 만들려고 한다.

 

data 형식은 아래와 같다.

var data = [
  {category: "USA", value: 2025}, 
  {category: "China", value: 1882}, 
  {category: "Japan", value: 1809}, 
	... 생략 ... 
  {category: "Germany", value: 1322}];

 

column-chart.js

var ColumnChart = function () {
  this.initEvents();
}

ColumnChart.prototype.initEvents = function () {

}

ColumnChart.prototype.getData = function (url, id) {
  var _this = this;

  $.ajax({
    url: url,
    method: "GET",
    dataType: "json",
    async: false,
    success: function(data) {
      _this.renderChart(id, data)
    },
    error: function(err) {
      alert("적용 중 에러가 발생하였습니다.");
      console.log(err);
    }
  });
}

ColumnChart.prototype.renderChart = function (chartId, data) {
  var root = am5.Root.new(chartId);

  root.setThemes([
    am5themes_Animated.new(root)
  ]);

  var chart = root.container.children.push(
    am5xy.XYChart.new(root, {
      panX: false,
      panY: false,
      layout: root.verticalLayout
    })
  );

  // y좌표 설정
  var yRenderer = am5xy.AxisRendererY.new(root, {});
  yRenderer.labels.template.setAll({
    fontSize: 13 // y좌표 폰트 크기 조정
  });

  var yAxis = chart.yAxes.push(
    am5xy.ValueAxis.new(root, {
      renderer: yRenderer
    })
  );

  // x좌표 설정
  var xRenderer = am5xy.AxisRendererX.new(root, { minGridDistance: 30 });
  xRenderer.labels.template.setAll({
    centerY: am5.p10,
    centerX: am5.percent(50),
    paddingRight: 0,
    fontSize: 13 // x좌표 폰트 크기 조정
  });

  var xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
    maxDeviation: 0.3,
    categoryField: "category", // 카테고리 명을 적어주면 된다.
    renderer: xRenderer,
  }));

  xAxis.data.setAll(data);

  var tooltip = am5.Tooltip.new(root, {
    labelText: "{valueY}",
    autoTextColor: false // 문자 색상을 설정할 수 있다. autoTextColor: false로 색상 설정을 없앰.
  });

  var series = chart.series.push(
    am5xy.ColumnSeries.new(root, {
      name: "Chart Name",
      xAxis: xAxis,
      yAxis: yAxis,
      valueYField: "value",
      categoryXField: "category", // x좌표 categoryField와 일치시킨다.
      tooltip: tooltip
    })
  );

  series.columns.template.setAll({
    cornerRadiusTL: 5,
    cornerRadiusTR: 5,
    width: 32 // 컬럼의 너비 설정
  });

  // 컬럼 색상 설정 -> 기본으로 설정
  series.columns.template.adapters.add("fill", function (fill, target) {
    return chart.get("colors").getIndex(series.columns.indexOf(target));
  });
  series.columns.template.adapters.add("stroke", function (stroke, target) {
    return chart.get("colors").getIndex(series.columns.indexOf(target));
  });

  series.data.setAll(data);
  chart.set("cursor", am5xy.XYCursor.new(root, {}));
}

 

index.html

<div class="category-chart">
 <div class="row">
  <div class="box">
   <div class="category-name">Category</div>
   <div id="category-chart" class="chart"></div>
  </div>
 </div>
</div>

<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
<script>
    var ColumnChart = new ColumnChart();

    $(function () {
      ColumnChart.getData('/category', 'category-chart'); // ajax로 data를 가져와 chart를 그려준다.
    })
</script>

 

chart.css

.category-chart {
  width: 100%;
}

.box {
  /* width: 48%; */
  background-color: rgba(238, 238, 238, 0.4);
  padding-top: 20px;
  padding-bottom: 16px;
  margin-left: auto;
  margin-bottom: 20px;
  border-radius: 10px;
}

.category-name {
  font-weight: bold;
  text-align: center;
  margin-bottom: 6px;
}

.chart {
  height: 300px; /* height를 꼭 지정해주어야 한다. */
}

 

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

자바스크립트를 사용하여 페이지를 새로고침하는 방법이다. 화면 개발을 하다보면 페이지 전체를 불러오거나 특정 영역을 갱신해야 하는 경우에 일반적으로 location을 사용한다. 특정 부분을 갱신하는 경우에는 iframe을 사용하거나 jQuery의 load를 사용하는 것이 좋다.

 

location.reload()

// location을 사용하는 방법
location.reload();
location.replace(location.href);
location.href = location.href;

// history를 사용하는 방법
history.go(0);

location은 페이지의 위치를 나타내기 때문에 location 방식을 권장한다. history는 페이지가 이동한 이력을 정의하고 있다. 

 

// Post 데이터를 포함해 페이지를 새로고침한다.
location.reload();

// Post 데이터는 포함하지 않으며 페이지를 새로고침한다.
// 이 때 현재 이력을 수정하며 페이지를 불러오기 때문에 history에 새로운 이력은 추가되지 않는다.
location.replace(location.href);

// 페이지를 이동한다. 이동할 페이지를 현재 페이지로 지정한다.
location.href = location.href;

reload() 함수는 옵션을 주어 실행할 수 있으며, 1개의 boolean 인자를 옵션 값으로 받는다. default 옵션값은 false이다. 

true로 설정하는 경우에는 브라우저가 가지고 있는 캐시를 무시하고 새로운 리소스를 화면에 불러온다.

location.reload(); // default: false

location.reload(true); // 브라우저가 가지고 있는 기존의 리소스는 신경쓰지 않고 새로운 리소스를 받아 화면을 갱신합니다.

 

location

location은 현재 Document에 대한 위치를 가지고 있는 객체로 location이나 window.location 또는 document.location를 통해서 접근이 가능하다.

 

location, window.location, document.location를 일치 연산자를 이용하여 비교해보면 동일하다는 것을 알 수 있다.

location === window.location // true
window.location === document.location // true

location, window.location, document.location은 동일하기 때문에 브라우저간의 호환성 문제가 있어 location의 사용에 문제가 있는 경우 아래와 같은 방법으로 페이지를 새로고침할 수 있다.

function reload() {
    (location || window.location || document.location).reload();
}
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

ModelMapper 라이브러리

 

의존성 추가

build.gradle

implementation 'org.modelmapper:modelmapper:2.4.2'

 

변환 클래스들 정의

// 모든 클래스 Constructor, Getter, Setter 생략
class Address {
    String street;
    String city;
}

class Name {
    String firstName;
    String lastName;
}

class Customer {
    Name name;
}

class Order {
    Customer customer;
    Address address;
}

class OrderDto {
    String customerFirstName;
    String customerLastName;
    String billingStreet;
    String billingCity;
}

 

ModelMapper.map(Object source, Class<D> destinationType)

Order order = new Order(
                new Customer(new Name("FIRSTNAME", "LASTNAME")),
                new Address("STREET", "CITY")
              );

ModelMapper modelMapper = new ModelMapper();

OrderDto result = modelMapper.map(order, OrderDto.class);
// result - customerFirstName = "FIRSTNAME"
// result - customerLastName = "LASTNAME"
// result - billingStreet = "STREET"
// result - billingCity = "CITY"

각 클래스 프로퍼티들의 연관관계를 자동으로 판단하여 매핑되었다.

 

이처럼 ModelMapper에서는 map(source, destination) 메서드가 호출되면 source와 destination의 타입을 분석하여 매칭 전략 및 기타 설정 값에 따라 일치하는 속성을 결정하게 된다. 그런 다음 결정한 매칭 항목들에 대해 데이터를 매핑하는 것이다.

위처럼 source와 destination의 객체 타입이나 프로퍼티가 다른 경우에도 설정된 매칭 전략에 따라서 최선의 매핑과정을 수행한다.

암시적으로 일치하는 프로퍼티들의 연관관계가 존재하지 않거나 이러한 연관관계가 모호한 경우, 매핑이 원활히 이루어지지 않을 수도 있다.

 

TypeMap

// 모든 클래스 Constructor, Getter, Setter 생략

class Item {
    private String name;
    private Integer stock;
    private Integer price;
    private Double discount;
}

class Bill {
    private String itemName;
    private Integer qty;
    private Integer singlePrice;
    private Boolean sale;
}
Item itemA = new Item("itemA", 10, 1500, true);
Bill bill = modelMapper.map(itemA, Bill.class);
// bill - itemName = "itemA"
//      - qty = null
//      - singlePrice = null
//      - discount = null

ModelMapper의 기본 매칭 전략으로는 모호한 연관관계들은 매핑되지 않는다.

 

따라서 ModelMapper에서는 위와 같은 문제를 해결하기 위해 Type Map 기능을 제공한다.

 

TypeMap<S, D>

TypeMap 인터페이스를 구현함으로써 매핑 설정을 캡슐화하여(Encapsulating)하여 ModelMapper 객체의 매핑 관계를 설정해줄 수 있다.

 

매핑 관계 추가

먼저 위의 예시에서 우리가 원하는 매핑 전략은 다음과 같다.

  • Item.stock -> Bill.qty
  • Item.price -> Bill.singlePrice
  • Item.sale -> Bill.discount

수량과 가격의 경우 아래와 같이 메서드 레퍼런스를 통해 간단히 설정 가능하다.

modelMapper.typeMap(Item.class, Bill.class).addMappings(mapper -> {
    mapper.map(Item::getStock, Bill::setQty);
    mapper.map(Item::getPrice, Bill::setSinglePrice);
});

Bill bill2 = modelMapper.map(itemA, Bill.class);
// bill2 - itemName = "itemA"
//       - qty = 10
//       - singlePrice = 1500
//       - discount = null

하지만 Item.sale, Bill.discount와 같이 클래스 타입이 다른 경우 추가적인 방법이 필요하다.

 

파라미터 타입 변환

매핑하려는 데이터의 source와 destination 타입이 다른 경우, Converter 인터페이스를 사용해 유연하게 값을 설정해줄 수 있다.

 

Item.sale == true일 경우 할인율 20.0으로 설정해준다고 가정하자

mapper.using(Converter<S, D>)와 같은 패턴을 사용하면 유연한 타입 변환이 가능하다. using은 말 그대로 다음과 같은 Converter 규칙을 사용하겠다는 것이다.

 

modelMapper.typeMap(Item.class, Bill.class).addMappings(mapper -> {
    mapper.map(Item::getStock, Bill::setQty);
    mapper.map(Item::getPrice, Bill::setSinglePrice);
    mapper.using((Converter<Boolean, Double>) context -> context.getSource() ? 20.0 : 0.0)
            .map(Item::isSale, Bill::setDiscount);
});

Bill bill2 = modelMapper.map(itemA, Bill.class);
// bill2 - itemName = "itemName"
//       - qty = 10
//       - singlePrice = 1500
//       - discount = 20.0

Converter를 통해 정상적으로 타입이 변환되었다.

 

매핑 skip하기

클래스의 특정 프로퍼티는 매핑이 이루어지지 않도록 설정하는 것도 가능하다.

modelMapper.typeMap(Item.class, Bill.class).addMappings(mapper -> {
    mapper.map(Item::getStock, Bill::setQty);
    mapper.map(Item::getPrice, Bill::setSinglePrice);
    mapper.using((Converter<Boolean, Double>) context -> context.getSource() ? 20.0 : 0.0)
            .map(Item::isSale, Bill::setDiscount);
    mapper.skip(Bill::setItemName); // skip 추가
});

Bill bill2 = modelMapper.map(itemA, Bill.class);
// bill2 - itemName = null
//       - qty = 10
//       - singlePrice = 1500
//       - discount = 20.0

Bill.itemName 값의 매핑이 임의로 스킵되었다.

 

null인 속성 값만 매핑 skip 하기

객체에 새로운 값들을 한번에 업데이트해줄 때, ModelMapper의 기본 매칭 전략을 사용하면 null까지 함께 업데이트가 되는 문제가 생기므로 이를 위해서 매핑 설정을 해줄 수 있다.

ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setSkipNullEnabled(true);

 

Validation

ModelMapper는 기본적으로 매칭 전략에 맞지 않는 속성들은 null 값으로 초기화하게 되는데 개발자의 입장에서 어떤 객체에 대해 모든 속성값들이 정상적으로 매핑되었는지 검증할 필요가 있다.

 

ModelMapper().validate()를 이용하여 매핑 검증이 실패하는 경우 예외 처리를 해주기 때문에 추가적인 예외 핸들링이 가능하다.

modelMapper = new ModelMapper();

Bill bill3 = modelMapper.map(itemA, Bill.class);

try {
    modelMapper.validate();
} catch (ValidatationException e) {
    // Exception Handling
}

 

Strategies

ModelMapper는 지능적인 오브젝트 매핑을 수행한다.

객체들의 매칭 전략을 하나하나씩 임의로 설정해 주어야 한다면 편의성을 위해서 ModelMapper 라이브러리를 사용하는 것이 아니게 되므로 특정 매칭 전략을 입력해 주지 않고도 다른 매칭 전략을 사용할 수 있게끔 추가적인 매칭 전략을 제공한다.

modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STANDARD); // STANDARD 전략
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE); // LOOSE 전략
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); // STRICT 전략

STANDARD

기본 매칭 전략으로서 STANDARD 전략을 사용하면 source 와 destination의 속성들을 지능적으로 매치시킬 수 있다.

  • 토큰은 어떤 순서로든 일치될 수 있다.
  • 모든 destination 속성 이름 토큰이 일치해야 한다.
  • 모든 source 속성 이름은 일치하는 토큰이 하나 이상 있어야 한다.

위 조건들을 충족하지 못하는 경우 매칭에 실패하여 null이 입력된다.

 

LOOSE

느슨한 매칭 전략으로 계층 구조의 마지막 destination 속성만 일치하도록 요구하여 source와 destination을 느슨하게 매치할 수 있다.

  • 토큰은 어떤 순서로든 일치될 수 있다.
  • 마지막 destination 속성 이름에는 모든 토큰이 일치해야 한다.
  • 마지막 source 속성 이름은 일치하는 토큰이 하나 이상 있어야 한다.

느슨한 일치 전략은 속성 계층 구조가 매우 다른 source, destination 객체에 사용하는데 이상적이다.

Order, OrderDto 와 같이 객체의 속성이 계층 구조를 가지는 경우

 

STRICT

엄격한 일치 전략으로, 불일치나 모호성이 발생하지 않도록 완벽한 일치 정확도를 얻을 수 있다. 하지만 source와 destination의 속성 이름들이 서로 정확하게 일치해야 한다.

  • 토큰은 엄격한 순서로 일치해야 한다.
  • 모든 destination 속성 이름 토큰이 일치해야 한다.
  • 모든 source 속성 이름에는 모든 토큰이 일치해야 한다.

STRICT 전략을 통해 앞에서 다룬 TypeMap 을 사용하지 않고도 모호함이나 예기치 않은 매핑이 발생하지 않도록 하는 경우에 간편하게 사용이 가능하다. 하지만 반드시 매칭되어야 하는 속성의 이름들이 서로 정확하게 일치해야 한다.

 

 

 

 출처 

https://devwithpug.github.io/java/java-modelmapper

 

ModelMapper 제대로 알고 사용하자!

개요

devwithpug.github.io

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

Windows는 ntfs와 fat 파일 시스템만 읽을 수 있지만 맥의 경우 모두 다 읽을 수 있다. 하지만 mac에서는 ntfs 파일시스템은 Read Only이기 때문에 Mounty 소프트웨어를 활용해서 쓸 수 있게 변경할 수 있다.

 

 

아래 명령어로 Mounty를 설치하고 실행해주면 된다. (Mac OS에 homebrew가 설치되어있어야 한다.)

$ brew install --cask mounty

https://formulae.brew.sh/cask/mounty

 

mounty

Homebrew’s package index

formulae.brew.sh

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

Document.referrer

링크를 통해 현재 페이지로 이동시킨, 전 페이지의 URI 정보를 반환

 

Syntax

string = document.referrer;

Note

페이지로 바로 접근하였을 경우 이 값은 빈 문자열을 반환함. 문자열만을 반환하기 때문에 참조 페이지(referring page)에 대한 DOM 액세스가 제공되지 않음

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !