[참조]; https://msdn.microsoft.com/en-us/library/ms178473.aspx?f=255&MSPPError=-2147217396


ASP.NET 의 라이프 사이클 관련 MSDN 설명입니다. 위 내용에 들어가 있는 ASP.NET 의 Life Cycle 그림입니다. 





'초짜 IT보이 서바이벌 스토리 > ASP.Net' 카테고리의 다른 글

#ASP.NET 의 #LifeCyle  (0) 2018.08.16
c#.Net Transaction Scope 사용하기  (0) 2010.04.27
ASP.NET MVC 패턴 소개 (C#)  (0) 2009.11.05
ASP.NET MVC 1.0  (0) 2009.04.20
ViewState 와 Session 의 장단점  (0) 2008.08.25
ASP.NET 공부시작~  (1) 2008.07.14

posted by Sungyoup Han matrim

Visual Studio 2010 에 NuGet Extension 을 설치하려고 하다보니 SP1 이 이미 설치되어 있어서 오류가 납니다.

해결방법은 Hotfix 적용입니다.


링크 : https://support.microsoft.com/ko-kr/hotfix/kbhotfix?kbnum=2581019&kbln=en-us


한국어로는 없고 모든 플랫폼 및 언어를 위한 핫픽스 보기를 누르면 리스트에 픽스가 보이고 선택 후 전자메일을 적으면 링크가 날아옵니다.

메일의 링크를 누르면 파일이 다운되고, 설치 파일 확인 후 설치가 진행 됩니다.


설치가 끝난 후 필요한 Extension 을 설치해 봤습니다.



Extension 이 잘 설치 되었네요. HOTFIX 필요하시면 꼭 다운받으세요

posted by Sungyoup Han matrim


Windows10 환경에서 visualStudio 2010 에 SpringFramework 닷넷 버전 1.3.2 의 Schema 설치를 위한 수정된 NAnt 스크립트 공유합니다.

바뀐 부분

SOFTWARE\Microsoft\VisualStudio\10.0_Config\InstallDir"

spring-database-1.3.xsd


<?xml version="1.0" ?>
<project name="Spring.NET-Schema" default="deploy-schema">

<target name="locations"
   descriptions="Shows directories where .xsd files will be copied">

  
  <property name="vsXXXX.version" value="VS.NET 2010"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\10.0_Config\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\..\Xml\Schemas"/>
<!-- retrieve location of VS.NET 2010 schema store -->
<readregistry property="vs2010.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="CurrentUser"
failonerror="false"/>
<echo message="VS.NET 2010 xsd install directory = ${vs2010.xml.schemas}${vsXXXX.relative.dir}"/> 

<property name="vsXXXX.version" value="VS.NET 2008"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\9.0\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\..\Xml\Schemas"/>
<!-- retrieve location of VS.NET 2008 schema store -->
<readregistry property="vs2008.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<echo message="VS.NET 2008 xsd install directory = ${vs2008.xml.schemas}${vsXXXX.relative.dir}"/>

<property name="vsXXXX.version" value="VS.NET 2005"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\8.0\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\..\Xml\Schemas"/>
<!-- retrieve location of VS.NET 2005 schema store -->
<readregistry property="vs2005.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<echo message="VS.NET 2005 xsd install directory = ${vs2005.xml.schemas}${vsXXXX.relative.dir}"/>

<property name="vsXXXX.version" value="VS.NET 2003"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\7.1\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\Packages\schemas\xml"/>
<!-- retrieve location of VS.NET 2003 schema store -->
<readregistry property="vs2003.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<echo message="VS.NET 2003 xsd install directory = ${vs2003.xml.schemas}${vsXXXX.relative.dir}"/>
</target>


<target name="deploy-schema"
description="Deploys the Schema to VS.Net schemas dirs. Copies VS 7.0, 7.1, 8.0, 9.0">

<!-- deploy spring schema to the VS.NET 2010 schema store -->
<property name="vsXXXX.version" value="VS.NET 2010"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\10.0_Config\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\..\Xml\Schemas"/>
<!-- retrieve location of VS.NET 2010 schema store -->
<readregistry property="vs2010.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="CurrentUser"
failonerror="false"/>
<property name="vsXXXX.xml.schemas.notinstalled" value="true"/>
<if test="${property::exists('vs2010.xml.schemas')}">
<property name="vsXXXX.xml.schemas" value="${vs2010.xml.schemas}"/>
<call target="_deploy-schema"/>
</if>
<if test="${vsXXXX.xml.schemas.notinstalled}">
<echo message=""/>
<echo message="Failed installing schema files for VS.NET 2010."/>
<echo message=""/>
</if>    
    
<!-- deploy spring schema to the VS.NET 2008 schema store -->
<property name="vsXXXX.version" value="VS.NET 2008"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\9.0\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\..\Xml\Schemas"/>
<!-- retrieve location of VS.NET 2005 schema store -->
<readregistry property="vs2008.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<property name="vsXXXX.xml.schemas.notinstalled" value="true"/>
<if test="${property::exists('vs2008.xml.schemas')}">
<property name="vsXXXX.xml.schemas" value="${vs2008.xml.schemas}"/>
<call target="_deploy-schema"/>
</if>
<if test="${vsXXXX.xml.schemas.notinstalled}">
<echo message=""/>
<echo message="Failed installing schema files for VS.NET 2008."/>
<echo message=""/>
</if>

<!-- deploy spring schema to the VS.NET 2005 schema store -->
<property name="vsXXXX.version" value="VS.NET 2005"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\8.0\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\..\Xml\Schemas"/>
<!-- retrieve location of VS.NET 2005 schema store -->
<readregistry property="vs2005.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<property name="vsXXXX.xml.schemas.notinstalled" value="true"/>
<if test="${property::exists('vs2005.xml.schemas')}">
<property name="vsXXXX.xml.schemas" value="${vs2005.xml.schemas}"/>
<call target="_deploy-schema"/>
</if>
<if test="${vsXXXX.xml.schemas.notinstalled}">
<echo message=""/>
<echo message="Failed installing schema files for VS.NET 2005."/>
<echo message=""/>
</if>


<!-- deploy spring schema to the VS.NET 2003 schema store -->
<property name="vsXXXX.version" value="VS.NET 2003"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\7.1\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\Packages\schemas\xml"/>
<!-- retrieve location of VS.NET 2003 schema store -->
<readregistry property="vs2003.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<property name="vsXXXX.xml.schemas.notinstalled" value="true"/>
<if test="${property::exists('vs2003.xml.schemas')}">
<property name="vsXXXX.xml.schemas" value="${vs2003.xml.schemas}"/>
<call target="_deploy-schema"/>
</if>
<if test="${vsXXXX.xml.schemas.notinstalled}">
<echo message=""/>
<echo message="Failed installing schema files for VS.NET 2003."/>
<echo message=""/>
</if>


<!-- deploy spring schema to the VS.NET 2002 schema store -->
<property name="vsXXXX.version" value="VS.NET 2002"/>
<property name="vsXXXX.xml.schemas.install.dir" value="SOFTWARE\Microsoft\VisualStudio\7.0\InstallDir"/>
<property name="vsXXXX.relative.dir" value="..\Packages\schemas\xml"/>
<!-- retrieve location of VS.NET 2003 schema store -->
<readregistry property="vs2002.xml.schemas" key="${vsXXXX.xml.schemas.install.dir}" hive="LocalMachine"
failonerror="false"/>
<property name="vsXXXX.xml.schemas.notinstalled" value="true"/>
<if test="${property::exists('vs2002.xml.schemas')}">
<property name="vsXXXX.xml.schemas" value="${vs2002.xml.schemas}"/>
<call target="_deploy-schema"/>
</if>
<if test="${vsXXXX.xml.schemas.notinstalled}">
<echo message=""/>
<echo message="Failed installing schema files for VS.NET 2003."/>
<echo message=""/>
</if>
</target>

<!-- 'private' scoped parameterised version of the 'deploy-schema' target -->


<target name="_deploy-schema" depends="_clean">

<!-- deploy schema to VS.NET XXXX schema store -->
<echo message="-------------------------------------------------------"/>
<echo message="Copying spring-objects-1.3.xsd to ${vsXXXX.version} installation."/>
<echo message="-------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-objects-1.3.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="-------------------------------------------------------"/>
<echo message="Copying spring-tool-1.1.xsd to ${vsXXXX.version} installation."/>
<echo message="-------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-tool-1.1.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
   overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-validation-1.3.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-validation-1.3.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-aop-1.1.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-aop-1.1.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-remoting-1.1.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-remoting-1.1.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-database.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-database-1.3.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-tx-1.1.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-tx-1.1.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-nms-1.3.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-nms-1.3.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-ems-1.3.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-ems-1.3.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<echo message="----------------------------------------------------------"/>
<echo message="Copying spring-nvelocity-1.3.xsd to ${vsXXXX.version} installation."/>
<echo message="----------------------------------------------------------"/>
<echo message=""/>
<copy file="spring-nvelocity-1.3.xsd" todir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}"
verbose="true"
overwrite="true"/>

<property name="vsXXXX.xml.schemas.notinstalled" value="false"/>
</target>

  <target name="_clean">
<echo message="-------------------------------------------------"/>
<echo message="Removing previously installed spring schema files"/>
<echo message="from ${vsXXXX.version} installation."/>
<echo message="-------------------------------------------------"/>
<echo message=""/>
    <delete failonerror="false">
      <fileset basedir="${vsXXXX.xml.schemas}${vsXXXX.relative.dir}">
        <include name="spring-*.xsd"/>
      </fileset>
    </delete>
  </target>
</project>


posted by Sungyoup Han matrim

posted by Sungyoup Han matrim

안녕하세요, 이전 글에서는 애자일 개발 방법론 (Agile Software Development ) 과 관련하여 숫자들을 통해 현재 트렌드 (Trend) 를  알아보았습니다.


< PIC from SiteReq >


이번글에서는 2018년 IT 내 개발 트렌드에 대해 알아보겠습니다. "6 Software Development Trends in 2018: Developers Needed" 라는 글이 2017, 11월에 Ben Butano 에 의해 올라왔는데요. 뭐 이 글이 모든 개발 트렌드를 대표 한다기 보다는, 제가 상세히 이야기 하고 싶은 내용이 포함되어 있기도 하고 다른 글에서도 많이 중복되는 내용이 들어 있어서, 인용을 하게 되었습니다. 

다음이 2018 년의 6가지 개발 트랜드 입니다. 

  1. 블록체인 개발 (Blockchain Development) 수요 증가
  2. IoT (Internet of Things) 과 Edge Computing - BizDevOps 
  3. 사이버보안 (Cyber-Security)의 변화
  4. 더이상 새롭지 않은 CD (Continuous Delivery)
  5. AI (Artificial Intelligence) 의 필요
  6. VR (Virtual Reality) 의 히트 예감

위의 내용을 이야기 하면서, 개발자들에게는 이와 관련된 내용을 알고, 기술을 습득해야 한다라는 내용의 글입니다. 

간략하게 내용을 살펴보면, 블록체인 개발의 수요는 폭팔적으로 증가할 것이라고 예상하고 있으며, IoT 기술은 정보를 받아드리는 네트워크의 가장 끝 부분에서 부터 체집한 정보를 분석 프로세싱 하는 Edge Computing 과 접목하여 데이터 프로세스 시간을 단축시키고, 리얼 타임분석 데이터 제공의 현실화를 가능하게 했다고 합니다. 이에 BizDevOps 를 받아들이는 비지니스 분야들이 많아 질 것으로 예상이 되며 이  BizDevOps 에 대해 좀더 알아볼 예정입니다. 

< PIC from Blueprintsys >


사이버 보안의 변화도 있을 예정인데, 정부, 기관, 회사 등 보안의 필요성에 대해서는 뼈저리게 알고 있으며, 이에 2018년에는 외부적인 보안 만이 아닌 소프트웨어 안의 내부적인 보안을 시작 한다고 합니다.  DevOps 팀에서는 보안 검사 자동화 (Automating Security Testing) 를 개발 방법론의 LifeCycle 에 추가하여 개발 진행 중에도 보안이 되도록 준비 해야 할 것입니다. 이 부분에서 DevOps 와 보안 검사 자동화 에 대해 좀 더 알아볼 예정입니다.

CD (Continuous Delivery) 가 더 이상 새롭지 않은 이유는, 당연히 모든 회사에서 받아들였고, 현재 시행 되고 있기 때문이라고 합니다. 참고로 CD 란, 개발자가 코드 작성을 완료 하고 나서, 코드 통합, 테스트, 빌드, 그리고 구성 설정까지 완료 하여, 마지막 배포준비완료 단계 까지의 과정을 지속적으로 반복하는 것입니다. Amazon 같은 큰 회사에서는 한번의 CD 가 11.7 초가 걸린다고 하네요. 아예 Continuous Deployment 까지 시행 하는 회사들도 있지만, 그 보다 바로 이전 단계인 CD 는 당연히 받아들여 시행 하는 회사들이 아주 많아졌다는 이야기 입니다. CD 를 위해선 코드 통합을 지속적으로 해주는 Continuous Integration 서버와 CD 전체를 모티터링하는 관리 툴 그리고 코드 관리 플랫폼 등, 자동화를 위한 툴 (Tool) 들이 몇가지 필요합니다. 애자일 개발 방법론에 의해 개발되어 CD 이기 때문에 당연히 Continuous Delivery 에 대해서도 좀더 알아볼 예정이네요.

2017년까지, AI 는 빅데이터 (Big Data) 와 모바일 등과 접목하여 큰 성과를 냈으며 앞으로도 데이터 사이언티스트 등의 수요를 꽤 오랫동안 높여 줄 기술이라고 예상합니다. VR 은 게임과 접목하여 상당한 첫인상을 사용자들에게 심어줬으며 2018년에는 시장 확장을 위해 Oculus, 나 Galaxy 기어 등 하드웨어의 가격이 내려갈 것으로 예상하고 있습니다. 

이렇게 2018년, 소프트웨어 개발의 트렌드를 살펴보았습니다. 이 글에서 언급되었듯이, 다음 내용들은 좀더 자세하게 다뤄볼 예정입니다.

  1. Continuous Delivery
  2. DevOps 와 보안 검사의 자동화 (Automating Security Testing)
  3. BizDevOps





posted by Sungyoup Han matrim

안녕하세요 오랜만에 개발 관련 글을 적게 됩니다. 몇 안되지만, 그동안 애자일 개발 프로세스를 사용했던 프로젝트 참여 경험과 호주 유학 시절 접했던 소프트웨어 개발 교육 내용을 토대로 적어 내려가며 최근 자료로 업데이트도 하고, 정리도 하며 공유 해 보려고 합니다. 

2018년 현재, 국내에서 Agile (이하 애자일) 개발 프로세스 를 처음 듣는 개발자나 관리자는 거의 없을 것이라 생각됩니다. (경험하지않은 분들은 당연히 있을것입니다.) 꽤 오랬동안 Hot 한 방법론(프로세스) 이였고, 여러 방법으로 이 프로세스를 개발 문화로 안착 시키려고 상당히 많은 분들이 노력 해 오셨습니다. 그래서 많은, 성공설도, 실패설도 있습니다. 

개발 방법은 전통적인 방법인 워터폴도 있고, 스파이럴 혹은 프로토타이핑 방법 등이 있는데 왜 애자일 개발 방법론을 선택하여 개발 해야 할까요? 10년전 필드에서는 벌써 전통적인 워터폴의 문제점들을 보안하고 변형시킨, 애자일 혹은 DevOps (이하 데브옵스) 등의 단어들을 사용하지 않더라도, 오랜 경험을 바탕으로 필요에 의해 내부적으로 개발 프로세스를 애자일 식으로 자연스레 변경한 팀 (Team) 들이 있었습니다. 하지만 대부분 워터폴 방법으로 개발 했고, 윗단계에서 발생한 문제점들은, 폭포수가 단계적으로 내려오며 마지막에 엄청난 량의 물이 떨어지듯, 엄청난 량의 문제점들과 이를 수습하기 위한 엄청난 비용, 노력, 밤샘 등의 반복이 있었습니다. 

때문에, [12 Annual State of Agile Report] 조사에 의하면 2017년엔 조사한 대상 회사 중, 97% 나 되는 회사에서 애자일 개발 방법론을 채택 했을 정도로 많이 확산되어 있습니다. 참고로 조사 대상 중 80% 이상이 미국과 유럽내 있는 회사들입니다. 그리고 이 방법론이 더욱 효과적으로 사용 하기 위해 지속적인 변형이 일어나고 있습니다. Scrum [56%] 방법이 가장 많이 선택되고 실행 되고 있지만, 필요에 따라 Scrum/XP 하이브리드 [6%], 나 ScrumBan [8%], 등 현재 소개된 애자일 개발 방법론들의 하이브리드 형태로 받아들여지고 있습니다. 

그 어떤 형태로든 애자일 개발 방법을 선택하여 진행된 프로젝트 중 성공 비율은 [12 Annual State of Agile Report] 조사 대상 중 98% 회사에서 애자일 개발 방법을 선택한 프로젝트의 성공 경험이 있다고 답변을 했습니다. 그리고 이 중 74% 는 반 이상의 애자일 프로젝트가 성공적으로 끝났다고 합니다. 

프로젝트 성공의 잣대는 역시나 주어진 시간 내, 계획을 잘 세워, 예상한 비용 만큼만 사용하여 모든 Scope 을 완료 하였다 일까요? 애자일에서는 이터레이션 번다운이 이를 설명하는 가장 근사치의 요소일 텐데요. 2016년만 하더라고 51%의 답변으로 가장 중요한 요소를 차지 했었습니다.  하지만, 올해엔 27%로 중요도가 떨어졌습니다. 

그 이유는 애자일 프로젝트가 성공적이라고 결정하는 가장 큰 요소가 2017년엔, 첫번째는 고객 만족도 이고, 비지니스의 가치 전달, 그리고 개발 후 마켓으로의 속도 순으로 바뀌었기 때문입니다. 여기서 고객 만족도는 2016년만 해도 28% 정도의 순위였는데 2017년에는 46%로 가장 중요한 요소가 되었습니다.

경험상, 어떤 고객(사용자)이든, 작게 나마 완성되어 돌아가는 프로그램을 지속적으로 보여주면서, 피드백을 받아주는 개발팀을 싫어하는 고객은 없었습니다. 결과물들을 더 자주 보기 위해 1 week Sprint (스프린트) 를 돌렸던 프로젝트도 있었던 것처럼 고객은 결과물을 자주 보고 피드백을 주며 그 피드백들이 대부분 적용되었으면 합니다. 여기서 개발팀은 Scope 이 늘어나기 때문에 초기 요청사항에서 동떨어진 피드백을 받기를 당연히 원하지 않습니다. 하지만 이를 위해 스프린트 (이터레이션) 마다 Story Card 의 우선순위를 바꾸는 회의가 진행됩니다. 고객(사용자)은 비지니스와 불필요한 Needs 에 대해 고집을 부리지 않습니다. 물론 같은 비용으로 더 많은 부분이 완료 되었으면 하지만, 불필요한 부분으로 인하여 정말 주요기능들이 빠지는 것을 원하지 않습니다. 우선순위 회의의 결과를 공유하여 문제 파악과 해결점을 함께 찾아야 합니다. 

여하튼, 요구사항이 바뀌는 것을 최소화 하기 위해 애자일에서는 Product Owner (PO) 가 늘 함께 해야 한다..는 실제로 불가능한 이론을 이야기 합니다. 분명 PO 의 참여도가 떨어진다라는 요소가 2016년 까지는 큰 장벽 요소 중 하나였지만, 2017년엔 31%로 큰 장벽의 요소에서는 제외 되었습니다. 아마도, 스프린트 끝의 회의 때마다 PO 에게 사용해 보게 한 뒤, 피드백을 받아, 점진적으로 고객의 Needs 에 대등한 완성도 높은 제품으로 변해가는 과정을 함께 경험/공유 하다 보면, PO 는 매 회의 때를 고대하게 되지 않을까 싶네요.

이렇게 많은 회사에서 사용중이라고 발표도 하고, 고객의 만족도가 높은 결과물을 낼 수 있는, 그리고 또 꽤 높은 성공률을 자랑하는 애자일 개발 방법론..

과연, 개발 팀원들은 (PO/개발/UI/BA/테스터) 한번 애자일을 경험한 후에 이 프로세스에 만족하며 이후에도 프로젝트를 위해 또 애자일 프로세스를 선택하게 될지 궁금합니다. 개인적으로는 무척 좋아했습니다. 짧은 스토리 카드 하나하나 완료 시키는 재미도 있고, 고객의 피드백이 늘 추가 요구사항이나 변경으로 이어지는것이 아니라, 후반으로 갈 수록 높은 만족도로 이어지는 경우가 대부분이라서 일을 하면서도 뿌듯했습니다.  

그렇다면 애자일 경험이 없는, 현재 개발 프로세스의 체계가 잘 잡혀 있지 않은 상태에서는 어떤 선택이 가장 좋을까요?

============

4년전, 2014년, 호주에서는 애자일 개발 프로세스를 소프트웨어 개발 방법론으로 채택한, 많은 프로젝트 들이 진행 되고 있었는데요. 당시 런칭 했던 많은 서비스들 중 Pizza Mogul 이라는 재미난 서비스가 있었습니다. Domino Pizza 를 통해, 소비자들이 피자를 디자인(도우,토핑 등) 할 수 있으며, 디자인된 피자가 다른 소비자들에게 판매가 되면, 그 이익금의 일부를 피자를 만든 소비자가 가지고 가는 서비스 입니다. 

Project Management 수업을 들을 때, Pizza Mogul 의 개발 아웃소싱 시 프로젝트 메니저가 선택한 개발 방법론은 Agile 개발 방법론이였으며 관련하여 수업에서 Agile 개발 경험을 공유해 주었습니다. 유학을 위해 호주 가기 전에 참여했던 Agile 개발 방법론을 채택했던 프로젝트들과 그리 다르진 않아서 조금 다행스럽기도 했습니다. 

흥미로웠던 점은 프레젠테이션이 끝나고, 질문 시간에, 제가 "워터폴이나 다른 여러 개발 방법론이 있는데 구지 Agile 을 택한 이유가 있나요?" 라는 질문에 "회사를 들어갔을 때 부터 개발 문화가 Agile 이여서 채택? 했을 뿐이다. 다른 개발 방법론은 경험해 보지 않아서 모르겠다." 라고 답변을 들었습니다. 실은 제가 초기에 개발 방법론을 이해 하며 개발 했던것이 아니였기 때문에 좋고 나쁘다를 판단 할 수 있는 데이터가 모자라 물어본 질문이였는데 의외로 그냥 사내 개발 문화로 자연스럽게 받아들였다는 대답을 들어 "당연한 것을" 물어봤나 하는 생각도 들었네요.

또한 "요구사항이 바뀌거나 늘어났을 텐데, 그에 대해서 고객과 어떤 조율이 이루어 졌나요?" 라는 질문에 대해서는 "매번 우선순위를 정했고, 고객과 공유하여 서비스를 위해 꼭 필요한 기능들에 우선순위를 정하였고, 변경을 수용했다. Scope 이 늘어나면 시간과 비용이 추가적으로 발생하는 점에 대해서 먼저 고객과 상호 이해가 되어야 한다... (생략)" 

============



<to be Continued...>


posted by Sungyoup Han matrim


Swift is like Kotlin : http://nilhcem.com/swift-is-like-kotlin/

'초짜 IT보이 서바이벌 스토리 > iOS - swift' 카테고리의 다른 글

SWIFT vs KOTLIN  (0) 2017.10.31
[swift] code log, day 5  (0) 2017.09.27
[swift] code log, day 4  (0) 2017.09.26
[swift] code log, day 3  (0) 2017.09.25
[swift] code log, day 2  (0) 2017.09.24
[swift] code log, day 1  (0) 2017.09.22

posted by Sungyoup Han matrim

iBooks 의 SWIFT 4 를 통해 언어 공부 중입니다. 이때 사용된 책 속의 코드 및 연습문제 등의 코드 로그입니다.



아래 코드들은 Github 에서도 다운받을 수 있습니다.

SWIFT_Playground_Excersice: https://github.com/youp-han/SWIFT_Playground_Excersice


//------------------------------------------------------------

// 코드 로그 입니다.

// Day 5


do {

    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")

    print(printerResponse)

} catch PrinterError.onFire{

    print("I'll just put this over here, with the rest of the fire.")

} catch let printerError as PrinterError{

    print("Printer error: \(printerError).")

} catch {

    print(error)

}


//optional try (try?) will convert result to an optional.

//so if the function throws an error, the error is discarded and

// result is nil

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")

let printerFailure = try? send(job: 1884, toPrinter: "Never Has Toner")


//defer


var fridgeIsOpen = false


let fridgeContent = ["milk", "eggs", "leftovers"]


func fridgeContains(_ food: String) -> Bool {

    fridgeIsOpen = true

    defer{

        fridgeIsOpen = false

    }

    

    let result = fridgeContent.contains(food)

    return result

}


fridgeContains("banana")

print(fridgeIsOpen)



//Generics

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item]{

    var result = [Item]()

    for _ in 0..<numberOfTimes {

        result.append(item)

    }

    return result

}

makeArray(repeating: "knock", numberOfTimes: 4)



enum OptionalValue<Wrapped>{

    case none

    case some(Wrapped)

}

var possibleInteger: OptionalValue<Int> = .none

print("possibleinteger = \(possibleInteger)")

possibleInteger = .some(100)

print("possibleinteger (100) = \(possibleInteger)")


func anyCommonElements<T: Sequence, U: Sequence> (_ lhs: T, _ rhs: U) -> Bool where T.Iterator.Element: Equatable, T.Itertor.Element == U.Iterator.Element{

    for lhsItem in lhs{

        for rhsItem in rhs{

            if lhsItem == rhsItem {

                return true

            }

        }

    }

    

    return false

}


anyCommonElements([1,2,3], [3])



'초짜 IT보이 서바이벌 스토리 > iOS - swift' 카테고리의 다른 글

SWIFT vs KOTLIN  (0) 2017.10.31
[swift] code log, day 5  (0) 2017.09.27
[swift] code log, day 4  (0) 2017.09.26
[swift] code log, day 3  (0) 2017.09.25
[swift] code log, day 2  (0) 2017.09.24
[swift] code log, day 1  (0) 2017.09.22

posted by Sungyoup Han matrim

iBooks 의 SWIFT 4 를 통해 언어 공부 중입니다. 이때 사용된 책 속의 코드 및 연습문제 등의 코드 로그입니다.



아래 코드들은 Github 에서도 다운받을 수 있습니다.

SWIFT_Playground_Excersice: https://github.com/youp-han/SWIFT_Playground_Excersice


//------------------------------------------------------------

// 코드 로그 입니다.

// Day 4

// Enumerationas and Structures

// Default 로 raw value 는 0 에서 시작하지만, 다음의 경우

// case ace = 1에서 시작함.


enum Rank: Int{

    case ace = 1

    case two, three, four, five, six, seven, eight, nine, ten

    case jack, queen, king

    func simpleDescription() -> String{

        switch self{

        case .ace:

            return "ace"

        case .jack:

            return "jack"

        case .queen:

            return "queen"

        case .king:

            return "king"

        default:

            return String(self.rawValue)

        }

    }

}


let ace = Rank.ace

let aceRawValue = ace.rawValue

let two = Rank.two

let twoRawValue = two.rawValue


if let convertedRank = Rank(rawValue:3) {

    let threeDescription = convertedRank.simpleDescription()

print(threeDescription)

}



//enum 2

enum Suit{

    case spades, hearts, diamonds, clubs

    func simpleDescription()->String{

        switch self{

        case .spades:

            return "spades"

        case .hearts:

            return "hearts"

        case .diamonds:

            return "diamonds"

        case .clubs:

            return "clubs"

        }

    }

    

    //experiment

    func color()->String{

        switch self{

        case .spades:

            return "black"

        case .clubs:

            return "black"

        default:

            return "red"

        }

    }

}


let hearts = Suit.hearts

let heartsDescription = hearts.simpleDescription()

let heartsColor = hearts.color()



// enum 3


enum ServerResponse {

    case result (String, String)

    case failure (String)

}


let success = ServerResponse.result("6:00 am", "8:09 pm")

let failure = ServerResponse.failure("Out of cheese")


switch success {

case let .result(sunrise, sunset):

    print("Sunrise is at\(sunrise) and sunset is at \(sunset).")

case let .failure(message):

    print("Failure... \(message)")

}


//struct

// struct vs class

// struct gets copied where classes are passed by reference


struct Card {

    var rank: Rank

    var suit: Suit

    func simpleDescription() -> String {

        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"

    }

}

let threeOfSpades = Card(rank: .three, suit: .spades)

let threeOfSpedesDescription = threeOfSpades.simpleDescription()



//Protocals and Estensions

//Classes, enum and Structs can adopt protocols


protocol ExampleProtocol {

    var simpleDescription : String { get }

    mutating func adjust()

}


class SimpleClass: ExampleProtocol{

    var simpleDescription: String = "A very simple class"

    var anotherProperty: Int = 52342

    func adjust(){

        simpleDescription += " Now 100% adjusted"

    }

}


var a = SimpleClass()

a.adjust()

let aDescription = a.simpleDescription


struct SimpleStruture: ExampleProtocol {

    var simpleDescription: String = "A simple structure"

    mutating func adjust(){

        simpleDescription += " (adjusted)"

    }

}


var b = SimpleStruture()

b.adjust()

let bDescription = b.simpleDescription


// why mutating func in Structure?

// because, the structure is a value type where class is a reference type

// and to make changes in a function of the structure,

// the function should be mutable

// example


struct Rectangle {

    var height = 1

    var width = 1


    func area() -> Int{

        return height * width

    }

    

    mutating func scaleBy(value: Int) {

        width*=value

        height*=value

    }

}


var rect = Rectangle()

// let rect = Rectangle() will give errors for its a value type (immutable)

rect.area()

rect.scaleBy(value: 3)

rect.area()



// extension of protocol


extension Int: ExampleProtocol {

    var simpleDescription: String {

        return "the number \(self)"

    }

    

    mutating func adjust(){

        self += 42

    }

}


print (7.simpleDescription)


let protocolValue: ExampleProtocol = a

print(protocolValue.simpleDescription)

//print(protocolValue.anotherProperty)


//Error Handling

enum PrinterError: Error{

    case outOfPaper

    case noToner

    case onFire

}


func send(job: Int, toPrinter printerName: String) throws -> String{

    if printerName == "Never Has Toner" {

        throw PrinterError.noToner

    }

    return "Job Sent"

}


do{

    // the following statement throws no error

    // let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")

     let printerResponse = try send(job: 1040, toPrinter: "Never Has Toner")

    print(printerResponse)

}catch{

    print(error)

}


'초짜 IT보이 서바이벌 스토리 > iOS - swift' 카테고리의 다른 글

SWIFT vs KOTLIN  (0) 2017.10.31
[swift] code log, day 5  (0) 2017.09.27
[swift] code log, day 4  (0) 2017.09.26
[swift] code log, day 3  (0) 2017.09.25
[swift] code log, day 2  (0) 2017.09.24
[swift] code log, day 1  (0) 2017.09.22

posted by Sungyoup Han matrim

iBooks 의 SWIFT 4 를 통해 언어 공부 중입니다. 이때 사용된 책 속의 코드 및 연습문제 등의 코드 로그입니다.



아래 코드들은 Github 에서도 다운받을 수 있습니다.

SWIFT_Playground_Excersice: https://github.com/youp-han/SWIFT_Playground_Excersice


//------------------------------------------------------------

// 코드 로그 입니다.

// Day 3


//Objects and Classes


class Shape{

    var numberOfSides = 0

    func simpleDescription() -> String {

        return "A shape with \(numberOfSides) sides."

    }


}




var shape = Shape()

shape.numberOfSides = 7

var shapeDescription = shape.simpleDescription();

print(shapeDescription)




//initializer

//when Initializing the class, the init variables must be declared

class NamedShape {

    var numberOfSides : Int = 0

    var name: String

    

    init(name: String){

        self.name = name

    }

    

    func simpleDescription() -> String{

        return "A Share with\(numberOfSides) sides, and the name is \(name)"

    }

}

var testname = NamedShape(name:"Mike")

print(testname.simpleDescription())



//superclass inherritance

class Square: NamedShape{

    var sideLength: Double

    init(sideLength: Double, name: String){

        self.sideLength = sideLength

        super.init(name: name)

        numberOfSides = 4

    }

    

    func area() -> Double {

        return sideLength * sideLength

    }

    

    override func simpleDescription() -> String {

        return "A square with sides of lengh \(sideLength)"

    }

}


let test = Square(sideLength: 5.2, name: "my Test Square")

print(test.area())

print(test.numberOfSides)

print(test.simpleDescription())



//excercise : area of Circle

class Circle: NamedShape{

    var radius: Double

    let pi: Double = 3.141592

    init(radius: Double, name: String){

        self.radius = radius

        super.init(name:name)

        numberOfSides = 0

    }

    

    func area() -> Double{

        return radius * radius * pi

    }

    

    override func simpleDescription() -> String {

        return "A circle with radius of \(radius)"

    }

}


var testCircle = Circle(radius: 4.0, name: "my Test Circle")

print(testCircle.area())

print(testCircle.simpleDescription())



//getter, setter of properties


class EquilateralTriangle: NamedShape{

    var sideLength: Double = 0.0

    

    init(sideLength: Double, name: String){

        self.sideLength = sideLength

        super.init(name: name)

        numberOfSides = 3

    }

    

    var perimeter: Double{

        get{

            return 3.0 * sideLength

        }

        set{

            sideLength = newValue / 3.0

        }

    }

    

    override func simpleDescription() -> String {

        return "An equilateral triangle with sides of length\(sideLength)"

    }

}


var triagle = EquilateralTriangle(sideLength: 3.1, name: "a Triagle")

print(triagle.perimeter)

triagle.perimeter = 9.9

print(triagle.sideLength)


class TriangleAndSquare{

    

    var triangle:EquilateralTriangle{

        willSet{

            square.sideLength = newValue.sideLength

        }

    }

    

    var square: Square{

        willSet{

            triagle.sideLength = newValue.sideLength

        }

    }

    

    init(size: Double, name: String){

        square = Square(sideLength: size, name: name)

        triangle = EquilateralTriangle(sideLength: size, name: name)

    }

}


var triangleAndSquare = TriangleAndSquare(size: 10, name: "Another Test Shape")

print(triangleAndSquare.square.sideLength)

print(triangleAndSquare.triangle.sideLength)

triangleAndSquare.square = Square(sideLength: 50, name: "Larger Square")

print(triangleAndSquare.triangle.sideLength)

print(triangleAndSquare.square.sideLength)



let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional Square")

let sideLength = optionalSquare?.sideLength

print("optional sidelength = \(sideLength)")


'초짜 IT보이 서바이벌 스토리 > iOS - swift' 카테고리의 다른 글

[swift] code log, day 5  (0) 2017.09.27
[swift] code log, day 4  (0) 2017.09.26
[swift] code log, day 3  (0) 2017.09.25
[swift] code log, day 2  (0) 2017.09.24
[swift] code log, day 1  (0) 2017.09.22
[swift] #SWIFT #4 언어 가이드  (0) 2017.09.22

posted by Sungyoup Han matrim



티스토리 툴바