레이블이 자바구글API인 게시물을 표시합니다. 모든 게시물 표시
레이블이 자바구글API인 게시물을 표시합니다. 모든 게시물 표시

2021년 12월 18일 토요일

[자바기초교육동영상]구글통계정보, 구글 레포트 API를 이용한 사이트 통계정보 가져오기, 연령구간대별,브라우저별,OS별,모바일디바이스 브랜드별로 페이지뷰, 접속사용자수, 접속세션수, 자바교육, 자바학원, 자바교육

 












[1단계]


구글 통계 정보를 조회해서 API를 이용해 데이터를 자지고 오려면


1. https://console.cloud.google.com/identity/serviceaccounts?folder=&organizationId=&project=top-creek-300922&supportedpurview=project

에서 사용자 추가, 아래 이메일이 그렇게 해서 만듦. 그리고 키파일 생성함

키파일은 자바 프로그램에서 사용하니 잘 보관해야 함


존재하지 않는 이미지입니다.

사진 삭제

사진 설명을 입력하세요.



2. 위 사용자에 대해 구글 애널리틱스의 관리자에서 조회권한을 줘야 함


존재하지 않는 이미지입니다.

사진 삭제

사진 설명을 입력하세요.


첫화면 좌하단 관리자 클릭

보기 à 보기 사용자관리 여기서 위에서 생성한 계정의 이메일을 통해 사용자 조회 권한을 부여함


존재하지 않는 이미지입니다.

사진 삭제

사진 설명을 입력하세요.



[2단계, 자바코드 작성하기]


1. 마리아DB 테이블


CREATE TABLE T_GA_STATS(

start_ymd VARCHAR(8) ,

end_ymd VARCHAR(8) ,

dimension VARCHAR(50),

dim_val VARCHAR(900),

page_views INT ,

users INT ,

sessions INT,

reg_dt DATETIME DEFAULT CURRENT_TIMESTAMP,

mdfcn_dt DATETIME DEFAULT CURRENT_TIMESTAMP,

PRIMARY KEY(start_ymd, end_ymd, dimension, dim_val)

)ENGINE = InnoDB CHARACTER SET utf8;



2. ga.properties


#VIEW ID, 구글 애놀리틱스 화면에서 좌상 단 "전체웹사이트 데이터" 선택 후 팝업화면에서 오른쪽 전체웹사이트 데이터아래 숫자 입니다.

view.id=22931XXXX


#Key File Location, 파일명은 사용자 마다 다릅니다. 위 1에서 사용자 등록하고 키파일 만든 바로 그것 입니다.

key.file.location=c:\\/top-creek-3030922-ca1xbabc22484.json


#----------------------------------- Metric(측정항목) Start

#페이지뷰 Expression

metric1.expression=ga:pageviews


#페이지뷰 alias

metric1.alias=pageviews


#사용자수 Expression

metric2.expression=ga:users


#사용자수 alias

metric2.alias=users


#세션수 Expression

metric3.expression=ga:sessions


#세션수 alias

metric3.alias=sessions

#----------------------------------- Metric(측정항목) End



#----------------------------------- Dimension(측정기준) Start

#성별

dim1=ga:userGender


#페이지 제목별

dim2=ga:pageTitle


#연령 구간대별

dim3=ga:userAgeBracket


#브라우저별

dim4=ga:browser


#OS별

dim5=ga:operatingSystem


#모바일 디바이스 브랜드별

dim6=ga:mobileDeviceBranding


#기기별

dim7=ga:deviceCategory

#----------------------------------- Dimension(측정기준) End


3. mybatis-config.xml


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD config 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>


<!-- DB설정 파일 로딩 -->

<properties resource="db.properties" />


<typeAliases>

<typeAlias alias="GaStatsVO" type="jmx.vo.GaStatsVO" />

</typeAliases>


<!-- db1 : 마리아DB 연결 -->

<environments default="development">

<environment id="development">

<transactionManager type="JDBC" />

<dataSource type="POOLED">

<property name="driver" value="${db1.driver}" />

<property name="url" value="${db1.url}" />

<property name="username" value="${db1.id}" />

<property name="password" value="${db1.password}" />

<property name="poolPingQuery" value="select 1"/> <!-- 핑쿼리 추가 -->

<property name="poolPingEnabled" value="true"/> <!-- 핑쿼리 추가 -->

<property name="poolPingConnectionsNotUsedFor" value="7200000"/> <!-- 2시간마다 -->

<property name="poolMaximumActiveConnections" value="200"/> <!-- 주어진 시간에 존재할수 있는 활성화된 커넥션 수 -->

<property name="poolMaximumIdleConnections" value="200"/> <!-- 주어진 시간에 존재할 수 있는 유휴 커넥션의 수 -->

<property name="poolTimeToWait" value="20000"/> <!-- 풀이 로그 상태를 출력하고 비정상적으로 긴경우 커넥션을 다시얻을려고 시도하는 로우 레벨 셋팅 -->

</dataSource>

</environment>

</environments>


<mappers>

<mapper resource="mapper/gastats.xml" />

</mappers>


</configuration>


4.

GaStatsVO.java


package jmx.vo;


import lombok.Data;


/* ----------------------------------------------------------------------------

* File Name : GaStatsVO.java

* Desc : 구글 통계정보를 저장하기 위한 VO

* ----------------------------------------------------------------------------

*/

@Data

public class GaStatsVO {

// 통계 시작년월일

private String start_ymd;

// 통계 종료년월일

private String end_ymd;

// 측정기준(성별, 브라우저별, OS별...... 접속현황, 이때 성별 이런것이 Dimension)

private String dimension;

// 측정기준값(male, female)

private String dim_val;

// 페이지뷰

private String page_views;

// 접속사용자수

private String users;

// 접속 세션수

private String sessions;

// 등록일시

private String reg_date;

// 수정일시

private String mdfcn_dt;

}


5. 매퍼 인터페이스(GaStatsMapper.java)


package jmx.db.mapper;


import jmx.vo.GaStatsVO;


/* ----------------------------------------------------------------------------

* File Name : GaStatsMapper.java

* Desc : 구글 통계정보 저장(T_GA_STATS)용 매퍼 인터페이스

* ----------------------------------------------------------------------------

*/

public interface GaStatsMapper {

// T_GA_STATS 입력(구글 통계정보)

public void insertGaStats(GaStatsVO gaStatsVO);

// T_GA_STATS 삭제(구글 통계정보)

public void deleteGaStats(GaStatsVO gaStatsVO);

}



6. DAO(GaStatsDAO.java)


package jmx.db.dao;


import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.log4j.LogManager;

import org.apache.log4j.Logger;


import jmx.db.mapper.GaStatsMapper;

import jmx.vo.GaStatsVO;


/* ----------------------------------------------------------------------------

* File Name : GaStatsDAO.java

* Desc : 구글 통계정보를 DB에 저장

* ----------------------------------------------------------------------------

*/

public class GaStatsDAO {


final static Logger logger = LogManager.getLogger(GaStatsDAO.class);

private SqlSessionFactory sqlSessionFactory = null;

private SqlSession session = null;


public void getSession() {

sqlSessionFactory = DBConnectionFactory.getSqlSessionFactory();

session = sqlSessionFactory.openSession();

}

/**

* 구글 통계정보 저장

* @param gaStatsVO

* @throws Exception

*/

public void insertGaStats(GaStatsVO gaStatsVO) throws Exception {


getSession();

GaStatsMapper mapper = session.getMapper(GaStatsMapper.class);

try {

mapper.insertGaStats(gaStatsVO);

}

catch(Exception e) {

session.rollback();

logger.info(">>>> insertGaStats Insert ERROR ::: " + e);

throw new Exception("insertGaStats Insert ERROR" + e.getMessage());

}

finally {

session.commit();

session.close();

session = null;

}

}

/**

* 구글 통계정보 삭제

* @param gaStatsVO

* @throws Exception

*/

public void deleteGaStats(GaStatsVO gaStatsVO) throws Exception {


getSession();

GaStatsMapper mapper = session.getMapper(GaStatsMapper.class);

try {

mapper.deleteGaStats(gaStatsVO);

}

catch(Exception e) {

session.rollback();

logger.info(">>>> deleteGaStats Delete ERROR ::: " + e);

throw new Exception("deleteGaStats Delete ERROR" + e.getMessage());

}

finally {

session.commit();

session.close();

session = null;

}

}


}



7. resource 아래 mapper 아래 매퍼 XML(gastats.xml)



<?xml version='1.0' encoding='UTF-8' ?>

<!DOCTYPE mapper PUBLIC '-//mybatis.org//DTD Mapper 3.0//EN'

'http://mybatis.org/dtd/mybatis-3-mapper.dtd'>


<mapper namespace='jmx.db.mapper.GaStatsMapper'>


<!-- 구글 통계정보 저장 -->

<insert id='insertGaStats' parameterType='GaStatsVO'>

INSERT INTO T_GA_STATS (

START_YMD

,END_YMD

,DIMENSION

,DIM_VAL

,PAGE_VIEWS

,USERS

,SESSIONS

)

VALUES(

#{start_ymd}

,#{end_ymd}

,REPLACE(#{dimension},'ga:', '')

,#{dim_val}

,CAST(#{page_views} AS UNSIGNED)

,CAST(#{users} AS UNSIGNED)

,CAST(#{sessions} AS UNSIGNED)

)

</insert>

<!-- 구글 통계정보 삭제 -->

<delete id='deleteGaStats' parameterType='GaStatsVO'>

DELETE FROM T_GA_STATS

WHERE START_YMD = #{start_ymd}

AND END_YMD = #{end_ymd}

</delete>

</mapper>



7. 구글 통계정보 읽기


package batch;


import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.security.GeneralSecurityException;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Calendar;

import java.util.GregorianCalendar;

import java.util.List;

import java.util.Properties;


import org.apache.log4j.LogManager;

import org.apache.log4j.Logger;


import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;

import com.google.api.client.http.HttpTransport;

import com.google.api.client.json.JsonFactory;

import com.google.api.client.json.gson.GsonFactory;

import com.google.api.services.analyticsreporting.v4.AnalyticsReporting;

import com.google.api.services.analyticsreporting.v4.AnalyticsReportingScopes;

import com.google.api.services.analyticsreporting.v4.model.ColumnHeader;

import com.google.api.services.analyticsreporting.v4.model.DateRange;

import com.google.api.services.analyticsreporting.v4.model.DateRangeValues;

import com.google.api.services.analyticsreporting.v4.model.Dimension;

import com.google.api.services.analyticsreporting.v4.model.GetReportsRequest;

import com.google.api.services.analyticsreporting.v4.model.GetReportsResponse;

import com.google.api.services.analyticsreporting.v4.model.Metric;

import com.google.api.services.analyticsreporting.v4.model.MetricHeaderEntry;

import com.google.api.services.analyticsreporting.v4.model.Report;

import com.google.api.services.analyticsreporting.v4.model.ReportRequest;

import com.google.api.services.analyticsreporting.v4.model.ReportRow;


import jmx.db.dao.GaStatsDAO;

import jmx.vo.GaStatsVO;


/* ----------------------------------------------------------------------------

* File Name : GAStatistics.java

* Desc : 구글 통계정보 가지고 와서 DB 저장

* ----------------------------------------------------------------------------

*/

public class GaStatistics {

// 화면 ID(22931XXXX로 고정)

private String viewId = null;

// 키파일 위치

private String keyFileLocation = null;

//----------------------------------- Metric(측정항목) Start

//측정항목1

private String metric1Expression = null;

private String metric1Alias = null;

//측정항목2

private String metric2Expression = null;

private String metric2Alias = null;

//측정항목3

private String metric3Expression = null;

private String metric3Alias = null;

//----------------------------------- Dimension Start

private String dim1 = null;

private String dim2 = null;

private String dim3 = null;

private String dim4 = null;

private String dim5 = null;

private String dim6 = null;

private String dim7 = null;


// GAStatistics 클래스가 new 될때 호출되는 초기화 블럭

{

// 구글 통뎨정보 가지고 오기 위한 설정 파일

loadProperties();

}


private String APPLICATION_NAME = "서구 Analytics Reporting";

private JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();


final static Logger logger = LogManager.getLogger(GaStatistics.class);

public static void main(String[] args) {

if (args.length < 2) {

System.out.println("기간(일별)을 입력하세요. java batch.GaStatistics 2014-01-01 2020-12-31");

System.exit(1);

}


GaStatistics ga = new GaStatistics();

//기간을 확인

logger.info("::::: " + args[0] + " 부터 " + args[1] + " 까지 구글 통계정보를 가지고 옵니다");

try {

ga.getData(args[0], args[1]);

logger.info("::::: " + args[0] + " 부터 " + args[1] + " 까지 구글 통계정보를 가지고 옵니다");

}

catch(Exception e) {

e.printStackTrace();

}

}


/**

* Get Google Analytics Statistics Data

* @throws Exception

*/

public void getData(String startDate, String endDate) throws Exception {

try {

AnalyticsReporting service = initializeAnalyticsReporting();


// 입력 기간 데이터 삭제

delete(startDate, endDate);

GetReportsResponse response1 = getReport1(service, startDate, endDate);

saveResponse(response1, startDate, endDate);

GetReportsResponse response2 = getReport2(service, startDate, endDate);

saveResponse(response2, startDate, endDate);

} catch (Exception e) {

logger.error(">>>>> 구글 애널리틱스 데이터 수신 오류! ::: " + e.getMessage());

throw new Exception(e);

}

}


/**

* Initializes an Analytics Reporting API V4 service object.

*

* @return An authorized Analytics Reporting API V4 service object.

* @throws IOException

* @throws GeneralSecurityException

*/

private AnalyticsReporting initializeAnalyticsReporting() throws GeneralSecurityException, IOException {


HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(keyFileLocation))

.createScoped(AnalyticsReportingScopes.all());


// Construct the Analytics Reporting service object.

return new AnalyticsReporting.Builder(httpTransport, JSON_FACTORY, credential)

.setApplicationName(APPLICATION_NAME).build();

}


/**

* Queries the Analytics Reporting API V4.

*

* 주의 : Request의 MAX는 5개, 즉 통계정보를 받기 위한 요청은 최대5개

* 그래서 getReport1(5개), getReport2(2개) 만듦

*

* @param service An authorized Analytics Reporting API V4 service object.

* @return GetReportResponse The Analytics Reporting API V4 response.

* @throws IOException

*/

private GetReportsResponse getReport1(AnalyticsReporting service, String startDate, String endDate) throws IOException {

// Create the DateRange object.

DateRange dateRange = new DateRange();

dateRange.setStartDate(startDate);

dateRange.setEndDate(endDate);


// Create the Metrics object.

Metric metric1 = new Metric().setExpression(metric1Expression).setAlias(metric1Alias); // 페이지뷰

Metric metric2 = new Metric().setExpression(metric2Expression).setAlias(metric2Alias); // 사용자수

Metric metric3 = new Metric().setExpression(metric3Expression).setAlias(metric3Alias); // 세션수

Metric metrics[] = new Metric[3];

metrics[0] = metric1;

metrics[1] = metric2;

metrics[2] = metric3;

Dimension dimension1 = new Dimension().setName(dim1); // 성별

Dimension dimension2 = new Dimension().setName(dim2); // 페이지제목별

Dimension dimension3 = new Dimension().setName(dim3); // 연령 구간대별

Dimension dimension4 = new Dimension().setName(dim4); // 브라우저별

Dimension dimension5 = new Dimension().setName(dim5); // OS별

//Dimension dimension6 = new Dimension().setName(dim6); // 모바일디바이스 브랜드별

//Dimension dimension7 = new Dimension().setName(dim7); // 기기(Device)별


// Create the ReportRequest object.

ReportRequest request1 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension1));

ReportRequest request2 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension2));

ReportRequest request3 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension3));

ReportRequest request4 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension4));

ReportRequest request5 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension5));

//ReportRequest request6 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

// .setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension6));

//ReportRequest request7 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

// .setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension7));


ArrayList<ReportRequest> requests = new ArrayList<ReportRequest>();

requests.add(request1);

requests.add(request2);

requests.add(request3);

requests.add(request4);

requests.add(request5);

//requests.add(request6);

//requests.add(request7);


// Create the GetReportsRequest object.

GetReportsRequest getReport = new GetReportsRequest().setReportRequests(requests);


// Call the batchGet method.

GetReportsResponse response = service.reports().batchGet(getReport).execute();


// Return the response.

return response;

}

/**

* Queries the Analytics Reporting API V4.

*

* 주의 : Request의 MAX는 5개, 즉 통계정보를 받기 위한 요청은 최대5개

* 그래서 getReport1(5개), getReport2(2개) 만듦

*

* @param service An authorized Analytics Reporting API V4 service object.

* @return GetReportResponse The Analytics Reporting API V4 response.

* @throws IOException

*/

private GetReportsResponse getReport2(AnalyticsReporting service, String startDate, String endDate) throws IOException {

// Create the DateRange object.

DateRange dateRange = new DateRange();

dateRange.setStartDate(startDate);

dateRange.setEndDate(endDate);


// Create the Metrics object.

Metric metric1 = new Metric().setExpression(metric1Expression).setAlias(metric1Alias); // 페이지뷰

Metric metric2 = new Metric().setExpression(metric2Expression).setAlias(metric2Alias); // 사용자수

Metric metric3 = new Metric().setExpression(metric3Expression).setAlias(metric3Alias); // 세션수

Metric metrics[] = new Metric[3];

metrics[0] = metric1;

metrics[1] = metric2;

metrics[2] = metric3;

Dimension dimension6 = new Dimension().setName(dim6); // 모바일디바이스 브랜드별

Dimension dimension7 = new Dimension().setName(dim7); // 기기(Device)별


// Create the ReportRequest object.

ReportRequest request6 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension6));

ReportRequest request7 = new ReportRequest().setViewId(viewId).setDateRanges(Arrays.asList(dateRange))

.setMetrics(Arrays.asList(metrics)).setDimensions(Arrays.asList(dimension7));


ArrayList<ReportRequest> requests = new ArrayList<ReportRequest>();

requests.add(request6);

requests.add(request7);


// Create the GetReportsRequest object.

GetReportsRequest getReport = new GetReportsRequest().setReportRequests(requests);


// Call the batchGet method.

GetReportsResponse response = service.reports().batchGet(getReport).execute();


// Return the response.

return response;

}

// 기간별 삭제

private void delete(String startDate, String endDate) throws Exception {

GaStatsVO vo = new GaStatsVO();

vo.setStart_ymd(startDate.replace("-", ""));

vo.setEnd_ymd(endDate.replace("-", ""));

// DB에 Mybatis를 통해 값을 저장하기 위한 DAO

GaStatsDAO dao = new GaStatsDAO();

// 혹시 있담녀 그날분 삭제

dao.deleteGaStats(vo);

}


/**

* Parses and prints the Analytics Reporting API V4 response.

*

* @param response An Analytics Reporting API V4 response.

* @throws Exception

*/

private void saveResponse(GetReportsResponse response, String startDate, String endDate) throws Exception {


GaStatsVO vo = new GaStatsVO();

vo.setStart_ymd(startDate.replace("-", ""));

vo.setEnd_ymd(endDate.replace("-", ""));

// DB에 Mybatis를 통해 값을 저장하기 위한 DAO

GaStatsDAO dao = new GaStatsDAO();

for (Report report : response.getReports()) {

ColumnHeader header = report.getColumnHeader();

List<String> dimensionHeaders = header.getDimensions();

List<MetricHeaderEntry> metricHeaders = header.getMetricHeader().getMetricHeaderEntries();

List<ReportRow> rows = report.getData().getRows();


if (rows == null) {

logger.info("No data found for " + viewId);

return;

}


for (ReportRow row : rows) {

List<String> dimensions = row.getDimensions();

List<DateRangeValues> metrics = row.getMetrics();


for (int i = 0; i < dimensionHeaders.size() && i < dimensions.size(); i++) {

vo.setDimension(dimensionHeaders.get(i));

if (dimensions.get(i).contains("(상세보기)")) {

vo.setDim_val(dimensions.get(i).substring(0, dimensions.get(i).indexOf("(상세보기)")-1));

}

else {

vo.setDim_val(dimensions.get(i));

}

if(dimensions.get(i).length() > 900) {

vo.setDim_val(dimensions.get(i).substring(0, 900));

}

}


for (int j = 0; j < metrics.size(); j++) {

DateRangeValues values = metrics.get(j);

for (int k = 0; k < values.getValues().size() && k < metricHeaders.size(); k++) {

if ("pageviews".equals(metricHeaders.get(k).getName())) {

vo.setPage_views(values.getValues().get(k));

}

else if ("users".equals(metricHeaders.get(k).getName())) {

vo.setUsers(values.getValues().get(k));

}

else if ("sessions".equals(metricHeaders.get(k).getName())) {

vo.setSessions(values.getValues().get(k));

}

}

}

try {

//VO에 있는 값을 저장

dao.insertGaStats(vo);

}

catch(Exception e) {

logger.error(">>>>> T_GA_STATS 저장 오류 ::: " + e);

e.printStackTrace();

throw new Exception("T_GA_STATS 저장 오류 ::: " + e);

}

}

}

}


/**

* 구글 통계정보를 가지고 오기 위한 설정파일 로딩

*/

public void loadProperties() {

InputStream input = null;

try {

input = this.getClass().getClassLoader().getResourceAsStream("ga.properties");

Properties prop = new Properties();

// load a properties file

prop.load(input);

// get the property value

viewId = prop.getProperty("view.id");

keyFileLocation = prop.getProperty("key.file.location");

metric1Expression = prop.getProperty("metric1.expression");

metric1Alias = prop.getProperty("metric1.alias");

metric2Expression = prop.getProperty("metric2.expression");

metric2Alias = prop.getProperty("metric2.alias");

metric3Expression = prop.getProperty("metric3.expression");

metric3Alias = prop.getProperty("metric3.alias");

dim1 = prop.getProperty("dim1");

dim2 = prop.getProperty("dim2");

dim3 = prop.getProperty("dim3");

dim4 = prop.getProperty("dim4");

dim5 = prop.getProperty("dim5");

dim6 = prop.getProperty("dim6");

dim7 = prop.getProperty("dim7");

} catch (Exception ex) {

ex.printStackTrace();

} finally {

if (input != null) {

try {

input.close();

input = null;

} catch (Exception e) {

}

}

}

}

}


#자바학원#자바교육#JAVA교육#JAVA학원 #자바학원교육#자바구글통계정보#구글통계정보#구글레포트#자바구글API#GoogleReport API#자바구글교육#자바사이트통계정보#자바접속통계#자바통계#자바구글API#자바동영상#JAVA동영상


자바학원자바교육JAVA교육JAVA학원 자바학원교육자바구글통계정보구글통계정보구글레포트자바구글APIGoogleReport API자바구글교



(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...