서론 - 사고

과제 리포지토리에서 프로젝트를 fork한 뒤 내 branch만 남기고 삭제하였다.
그 후 사용하던 notion-clone이라는 리포지토리에 clone땡기고 push mirror명령어를 이용하여 커밋내역째 push함.
이후 사태 발생.
과제 리포지토리 브랜치가 다 삭제됨...
처음에는 모든 pr이 닫혔길래 버그인 줄 알았으나 브랜치가 모두 삭제되었다는 사실을 알게됨.
다행히 restore를 할수있어서 브랜치들을 복구했으나 총 7분의 브랜치가 복구되지 않는 사실 발생
7분의 공통점을 찾아본 결과 fork하신 분들의 브랜치였음. 여쭤보니 다행히 7분중 6분은 fork하신 브랜치가 최신이라고 하셨다.
남은 한분의 답변만 기다리는중...😥

사고에서 배운 점

내가 100%알기 전까지 안다고 생각하지말자...중요한건 어린아이에게 설명할수 있는 수준까지 공부해놓자...!!


데이터 조인

번외 - 데이터

데이터의 타입은 다양하다. text, xml, csv, tsv, json... D3는 생각보다 더 많은 종류의 데이터를 지원한다.

d3.데이터타입('주소') 

이렇게 가져오면 데이터를 잘 가져와준다. 어떻게 가공해서 사용할지는 순전히 개발자의 몫이다.
참고로 tsv,csv는 가공이 잘 되어있다면[{},{}...]형태로 잘 가져와 주는 듯 보인다.

join - enter

selection객체 메서드중 DOM조작과 비슷한 기능이 아닌 D3만의 고유한 메서드 중 하나! 사실상 핵심이라고 보면 된다

enterupdate 그리고 exit를 사용한다.
=> data를 넣고, 업데이트하고 데이터를 제거(시야에서 사라지게 함)한다.
이 세가지를 아우르는 메서드가 selection.join(enter,[,update][,exit])이다.
참고로 join메서드는 요소를 정렬하여 반환해준다.

svg요소가 없는 상태에서도 data를 먼저 추가해놓을수 있다. 만약 rect가 없는 상황에서도...

//rect가 없을땐 null을 반환한다. 하지만 여기에 먼저 data를 체이닝해놓을 수 있다.
svg.selectAll('rect').data(someData)

이렇게 data를 먼저 달아놓고 svg를 생성할 수도 있다 ㅎㅎ

svg.selectAll('rect').data(someData).join('rect')

이러면 이제 데이터가 바인딩된 빈 객체svg요소인 rect가 추가된다. 이게 3개중 하나인 enter이다.

아래처럼 함수도 전달할 수 있다.

svg.selectAll('rect').data(someData).join((enter) => enter.append('rect').attr('fill','green'))

join - update, exit

//3개의 초록색 rect가 추가됨
svg.selectAll('rect').data(threeData).join(
  (enter) => enter.append('rect').attr('fill','green'), 
)

//3개의 초록색 rect에 1개의 주황색 rect가 추가됨 = 4개
svg.selectAll('rect').data(fourData).join(
  (update) => update.attr('fill').attr('fill','orange'),
)

//4개중 2개의 rect를 파란색으로 바꾼 뒤 삭제함
svg.selectAll('rect').data(twoData).join(
  (exit) => exit.attr('fill','blue').remove(),
)

또한 for문도 대체할 수 있다.

someEl.attr('x', (d,i) => d * foo).text((d,i) => !!d && i*2);

함수의 인자로 data, index가 들어오나보다!

key 함수

selection 객체도 객체다. 객체는 불변성을 유지해야 추적도 쉽고 관리하기 쉽다.

selection.data(fourData, (d) => d.name);

위처럼 두번째 인자로 키 값을 설정하는 함수를 전달할 수 있다. 기본값은 데이터의 인덱스다.
리액트의 key프로퍼티와 비슷하다. 각 데이터를 구분하기 위해 존재한다.


척도, 축

선형 척도(linear scale)

const x = d3.scaleLinear();
x.domain([0,10]);
x.range([0,100]);

x(0) // 0
x(10) //100

정의역(domain)에 10을넣고, 범위가 0~100이다. 따라서 y = x * 10의 함수가 된다.

이건 어디에 쓸까? 바로 척도비율을 맞출때 사용한다.

예를들어 정량적 데이터의 최소값이 1632고 최댓값이 4890이라고 생각해보자.
참고로 y축은 0부터 시작하는게 좋다고 하셨으니 이런 비율을 만들 수 있을 것이다.

const x = d3.scaleLinear()
.domain([0, d3.max(data, (d) => d.value)]) //데이터 중 최댓값(4890)을 정의역의 최댓값으로 둔다
.range([0, width]) //보여주고싶은 너비가 치역의 최댓값이다.

이러면 y = x * (width/데이터의 값) 의 비율이 나오게 된다.

순서형(순위) 척도, 묶음 척도(ordinal scale, band scale)

//순서형 척도
const ordinal = d3.scaleOrdinal()
.domain([1,2,3,4,5]) //정의역의 각 인덱스가 치역의 각 인덱스와 매핑된다.
.range([6,7,8,9,10]);

ordinal(1) // 6
ordinal(3) // 8

//묶음 척도
const band = d3.scaleBand()
.domain([1,2,3,4,5]) //치역 범위를 doamin의 길이로 나눈값을 하나씩 가지게된다
.range([1,100]);

band(1) // 1 시작점을 반환한다
band(5) // 80.2
band.bandwidth()// 19.8

영역, 여백

g태그 기준으로 너비, 높이 정하고 margin을 준다.

const margin = {top:30, right:50, bottom:0, left:150};
const width= 400;
const heigth = 170

const svgGroup = body.append('svg')
.attr('width', width+margin.left+margin.right);
.attr('height', height + margin.top + margin.bottom);
//아래부터는 g태그를 더하고 g태그의 위치를 옮기는 코드다 헷갈리지 말자
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

//x축
const xAxis = d3.axisTop(x) //x는 선형스케일에서 제작한 x값이다
.ticks(4) //4개의 틱으로 나눈다
.tickFormat((d,i) => {return `${!!d ? Math.floor(d/10000) : d}만명`}); //나눈 틱에 붙어있는 텍스트

svgGroup.append('g').call(xAxis);
//y축
const yAxis = d3.axisLeft(y) //y는 밴드스케일에서 제작한 y값이다

svgGroup.append('g').call(yAxis);

이쯤되면 selection객체가 왜 필요한 지 알것 같다. 데이터를 가지고 있으므로 메서드 내부에서 (d,i)이런식으로 데이터와 인덱스를 활용할 수 있다. 뿐만 아니라 척도나 축 등을 직접 설정할 수도 있다. 생각보다 훨씬 자세하게 구현이 가능할 것 같음!


마무리

  • 데이터의 join에 대해서 배웠다. (enter, update, exit)
  • D3의 척도와 축 관련 메서드에 대해 배웠다. (선형, 순서형, 묶음형, x-y축)
  • 데이터를 가져오는 법에 대해 배웠다.
  • 실제로 적용하는 법에 대해 배웠다.
profile
모르는 것을 모른다고 하기

0개의 댓글

Powered by GraphCDN, the GraphQL CDN